package com.yss.sofa.report.engine.util;

import java.awt.Point;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFPalette;
import org.apache.poi.hssf.usermodel.HSSFPrintSetup;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.FontUnderline;
import org.apache.poi.ss.usermodel.Footer;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Header;
import org.apache.poi.ss.usermodel.PaperSize;
import org.apache.poi.ss.usermodel.PrintSetup;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFColor;
import org.apache.poi.xssf.usermodel.XSSFFont;
import org.apache.poi.xssf.usermodel.XSSFPrintSetup;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.extensions.XSSFCellBorder.BorderSide;

import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Chunk;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.FontFactory;
import com.itextpdf.text.Image;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.RectangleReadOnly;
import com.itextpdf.text.pdf.BadPdfFormatException;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.ColumnText;
import com.itextpdf.text.pdf.FontSelector;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfCopy;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPCellEvent;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfPageEventHelper;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.PdfTemplate;
import com.itextpdf.text.pdf.PdfWriter;
import com.yss.sofa.report.engine.Const;
import com.yss.sofa.report.engine.Report;
import com.yss.sofa.report.engine.cell.page.PageCell;
import com.yss.sofa.report.engine.cell.visible.result.ChartResultCell;
import com.yss.sofa.report.engine.cell.visible.result.FloatingResultCell;
import com.yss.sofa.report.engine.cell.visible.result.ResultCell;

import net.coobird.thumbnailator.Thumbnails;

/**
 * @author 经春林
 */
public final class PdfUtil {

	private PdfUtil() {

	}

	/**
	 * 初始化
	 */
	public static void init() {
		// 完成字体加载
		ReportExcelPdfTable.getFont(null, true);
	}

	/**
	 * 将report对象转换为pdf字节数组.
	 * 
	 * @param report
	 *            report对象
	 * @return byte[]
	 * @throws Exception
	 */
	public static byte[] toPdf(Report report) throws Exception {
		if (!isValidReport(report)) {
			return new byte[0];
		}
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		try {
			export(report, out);
			out.flush();
			return out.toByteArray();
		} finally {
			out.close();
		}
	}

	/**
	 * 将report对象转换为pdf文件.
	 * 
	 * @param report
	 *            report对象
	 * @param filename
	 *            文件名(可包含路径)
	 * @throws Exception
	 */
	public static void export(Report report, String filename) throws Exception {
		FileOutputStream out = new FileOutputStream(new File(filename));
		try {
			export(report, out);
			out.flush();
		} finally {
			if(out!=null){
				out.close();
			}
		}
	}

	/**
	 * 将report对象转换为pdf字节导出到指定输出流中.
	 * 
	 * @param report
	 *            report对象
	 * @param out
	 *            输出流
	 * @param inputSheet
	 *            指定的sheet.如果为null,则使用report.main
	 * @throws Exception
	 */
	public static void exportSingle(Report report, OutputStream out, Sheet inputSheet) throws Exception {

		if (!isValidReport(report)) {
			Document doc = new Document(PageSize.A4);
			PdfWriter writer = PdfWriter.getInstance(doc, out);
			writer.setPdfVersion(PdfWriter.PDF_VERSION_1_7);
			doc.open();
			writeErrorTip(writer, doc, "This is a invalid document.", null);
			doc.close();
			return;
		}

		Workbook excel = report.getExcel(false); //toExcel(copy(report.getMiddle()));

		// report.getOutput()是原始的main对应的sheet, report.getName()是结果对应的sheet
		Sheet sheet = inputSheet != null ? inputSheet : excel.getSheet(report.getSheetMapping().get(Const.MAIN));
		
		ExcelWorkbook workbook = new ExcelWorkbook(excel);

		Document doc = getPdfDocumentBySheetPrintSetup(sheet);
		
		PdfPHFEvent event = getPdfPHFEvent(report, sheet, doc);
		
		PdfWriter writer = null;
		ByteArrayOutputStream baos = null;
		boolean isError = false;
		if (event != null) {
			if (event.hasPageTotalNumber) {
				 baos = new ByteArrayOutputStream();
			     writer = PdfWriter.getInstance(doc, baos);
			} else {
				writer = PdfWriter.getInstance(doc, out);
				writer.setPageEvent(event);
			}
		} else {
			writer = PdfWriter.getInstance(doc, out);
		}
		
		// default 1.4 version. it’s good practice to make setting the PDF version a part of your initializations to avoid ambiguity.
		writer.setPdfVersion(PdfWriter.PDF_VERSION_1_7);
		
		doc.open();

		try {
			String reportName = report.getSheetMapping().get(Const.MAIN);
			if (report.getRowCount(reportName) == 0 && report.getColCount(reportName) == 0) {
	    		writer.setPageEmpty(false);
			} else {
				// 自定义绘制
				new ReportExcelPdfTable(report, workbook, writer, doc).drawPdf(new SpecialRowSplitStrategy());
			}
		} catch (Exception e) {
			e.printStackTrace();
			
			writeErrorTip(writer, doc, "Appears wrong when document drawing process.", e);
			
			isError = true;
		}

		doc.close();
		
		// 处理总页数
		if (baos != null) {
		    PdfReader reader = new PdfReader(baos.toByteArray());
			PdfStamper stamper = new PdfStamper(reader, out);
			
			if (!isError) {
				int n = reader.getNumberOfPages();
				for (int i = 1; i <= n; i++) {
					// stamper.getWriter();
					event.drawPHF(doc, stamper.getOverContent(i), i, n);
				}
			}
			
			stamper.close();
		}
	}
	
	/**
	 * 将report对象转换为pdf字节导出到指定输出流中.(支持多张报表合并输出)
	 * 
	 * @param report
	 *            report对象
	 * @param out
	 *            输出流
	 * @throws Exception
	 */
	public static void export(Report report, OutputStream out) throws Exception {
		List<Report> reportList = getExportReportList(report);
		if (reportList == null || reportList.size() == 1) {
			exportSingle(reportList.get(0), out, null);
		} else {
			export(reportList, out);
		}	
	}
	
	@SuppressWarnings("unchecked")
	private static List<Report> getExportReportList(Report report) throws Exception {
		if (!isValidReport(report)) {
			return null;
		}
		
		Workbook excel = report.getExcel(false);
		
		Constructor<Report> constructor = Report.class.getDeclaredConstructor(new Class[0]);
		constructor.setAccessible(true);
		Field middleField = Report.class.getDeclaredField("middle");
		middleField.setAccessible(true);
		Field outputField = Report.class.getDeclaredField("output");
		outputField.setAccessible(true);
		Field matrixsField = Report.class.getDeclaredField("matrixs");
		matrixsField.setAccessible(true);
		Field imagesField = Report.class.getDeclaredField("images");
		imagesField.setAccessible(true);
		Field excelField = Report.class.getDeclaredField("excel");
		excelField.setAccessible(true);
		
		List<Report> reportList = new ArrayList<Report>();
		
		for (Iterator<String> iter = report.getSheetMapping().keySet().iterator(); iter.hasNext(); ) {
			String templateName = iter.next(); // main, main.a, main.b
			if (Const.FORM.equalsIgnoreCase(templateName) || Const.STYLE.equalsIgnoreCase(templateName)) {
				continue;
			}
			String reportName = report.getSheetMapping().get(templateName);

			
			Report r = constructor.newInstance(new Object[0]);
			
			r.getDatas().putAll(report.getDatas());
			r.getParams().putAll(report.getParams());
			r.getRawParams().putAll(report.getRawParams());
			r.getConsts().putAll(report.getConsts());
			
			PageCell pageCell = report.getPage(reportName);
			r.add(pageCell, report.getCells(reportName));
			r.getSheetMapping().put(Const.MAIN, reportName);
			
			outputField.set(r, reportName/*Const.MAIN*/);
			middleField.set(r, report.getMiddle());
			matrixsField.set(r, matrixsField.get(report));
			excelField.set(r, excel);
			
			Map<ResultCell, byte[]> images = report.getImages(reportName);
			Map<String, Map<ResultCell,byte[]>> toImages = (Map<String, Map<ResultCell,byte[]>>) imagesField.get(r);
			if (images == null || images.size() == 0) {
				images = new HashMap<ResultCell, byte[]>(0);
			}
			toImages.put(Const.MAIN, images);
			toImages.put(reportName, images);
			
			reportList.add(r);
		}
		
		return reportList;
	}
	
	/**
	 * 将report对象转换为pdf字节导出到指定输出流中.(支持多张报表合并输出)
	 * 
	 * @param reportList
	 *            report集合对象
	 * @param out
	 *            输出流
	 * @throws Exception
	 */
	public static void export(List<Report> reportList, OutputStream out) throws Exception {
		// 判断raws, 假设为多个
		Document document = new Document();
		PdfCopy copy = new PdfCopy(document, out);
		document.open();
		
		for (int i = 0, j = reportList.size(); i < j; ++i) {
			Report report = reportList.get(i);
			
			ByteArrayOutputStream stream = new ByteArrayOutputStream();
			
			Workbook excel = report.getExcel(false);
			Sheet sheet = excel.getSheet(report.getSheetMapping().get(Const.MAIN));
			exportSingle(report, stream, sheet);
			
			PdfReader reader = new PdfReader(stream.toByteArray());
			
			mergeContent(copy, reader);
			
			reader.close();
			
			stream.close();
		}
		
		document.close();
		copy.close();
	}
	
	/**
	 * 合并PDF内容
	 * 
	 * @param copy
	 * @param reader
	 * @throws BadPdfFormatException
	 * @throws IOException
	 */
	public static void mergeContent(PdfCopy copy, PdfReader reader) throws BadPdfFormatException, IOException {

		for (int i = 0, n = reader.getNumberOfPages(); i < n; ) {
			copy.addPage(copy.getImportedPage(reader, ++i));
		}
	}
	
	/**
	 * 追加信息到页面
	 * @param writer
	 * @param doc
	 * @param tip
	 * @param ex
	 */
	private static void writeErrorTip(PdfWriter writer, Document doc, String tip, Exception ex) {
		PdfContentByte canvas = writer.getDirectContent();
		canvas.saveState();
		
		try {
    		if (tip == null) {
    			tip = "";
    		}
    		
    		if (ex != null) {
    			String msg = ex.getMessage();
    			if (msg == null) {
    				StringBuilder stringBuilder = new StringBuilder();
		            StackTraceElement[] trace = ex.getStackTrace();
		            for (int i=0; i < trace.length; i++) {
		            	stringBuilder.append("\n\tat " + trace[i]);
		            }
		            msg = stringBuilder.toString();
    			}
    			
    			tip = tip  + "\nError Trace: \n" + ex.getClass().getName() + "\n" + msg;
    		}

    		float fontSize = 16;
    		FontSelector selector = getDefaultFontSelector(fontSize, BaseColor.RED);
    		
    		Phrase phrase = selector.process(tip);
    		
    		Rectangle rect = doc.getPageSize();

    		ColumnText ct = new ColumnText(canvas);
    		ct.setSimpleColumn(phrase, doc.left(), doc.bottomMargin(), 
    		                   rect.getWidth() - doc.leftMargin() - doc.rightMargin(), 
    		                   rect.getHeight() - doc.topMargin() - doc.bottomMargin(),
    		                   phrase.getLeading(),
			                   Rectangle.ALIGN_LEFT);
    		ct.go();
    		
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		canvas.restoreState();
	}
	
	/**
	 * 设置页眉/页脚. (如果包含页眉页脚，则页边距取页眉页脚边距，页眉页脚高度是页边距-页眉页脚边距)<br/>
	 * 页眉/页脚按Excel中页眉/页脚设置, 分左中右3个部分, 左边对齐方式top-left, 中间是top-center, 右边是top-right<br/>
	 * Excel打印预览中页边距中的居中方式不处理, 页眉页脚都是左中右3个部分, 奇偶页/首页不同不处理
	 * 
	 * @param report
	 * @param sheet
	 * @param doc
	 * @see com.yss.sofa.report.engine.Engine#generateMiddle
	 * @see com.yss.sofa.report.engine.util.PrintUtil#processPlaceholder 不需要解析vars, 传入的值都是处理好的. 日期时间统一参考
	 * @see 参考文档附录A-page单元格
	 */
	private static PdfPHFEvent getPdfPHFEvent(Report report, Sheet sheet, Document doc) {
		
		if(!hasPHF(report, sheet)) {
			return null;
		}

		// 如果包含页眉页脚，则页边距取页眉页脚边距，页眉页脚高度是页边距-页眉页脚边距
		// Gets the size of the margin in inches. Needs covert to Pt.
		float fac = UNIT_INCH_TO_PT_FAC;
		//float headerMargin = (float) (sheet.getMargin(XSSFSheet.HeaderMargin) * fac);
		//float footerMargin = (float) (sheet.getMargin(XSSFSheet.FooterMargin) * fac);
		
		// 如果page.isIncludePHF()为true, 那么就减去HeaderMargin/FooterMargin, 不然页眉和页脚高度等于TopMargin/BottomMargin
		float headerHeight = (float) ((sheet.getMargin(Sheet.TopMargin) - sheet.getMargin(Sheet.HeaderMargin)) * fac);
		float footerHeight = (float) ((sheet.getMargin(Sheet.BottomMargin) - sheet.getMargin(Sheet.FooterMargin)) * fac);
		
		// Excel默认字体 - Excel默认是"等线 Light 11"
		Font firstFont =null;
		if(sheet instanceof XSSFSheet){
			XSSFFont defaultFont = ((XSSFSheet)sheet).getWorkbook().getFontAt((short) 0);
			firstFont = ReportExcelPdfTable.getFontByExcel(defaultFont);
		}else if(sheet instanceof HSSFSheet){
			HSSFFont defaultFont = ((HSSFSheet)sheet).getWorkbook().getFontAt((short) 0);
			firstFont = ReportExcelPdfTable.getFontByExcel03(defaultFont,(HSSFSheet)sheet);
		}
		
		float firstFontSize = firstFont.getSize();
		Font f2 = FontFactory.getFont(FONT_STSONG, FONT_ENCODING_UNIGB_UCS2_H, BaseFont.NOT_EMBEDDED, firstFontSize); // 简体宋体
		Font f3 = FontFactory.getFont(FONT_MSUNG, FONT_ENCODING_UNICNS_UCS2_H, BaseFont.NOT_EMBEDDED, firstFontSize); // 繁体宋体
		f2.setColor(firstFont.getColor());
		f2.setStyle(firstFont.getStyle());
		f3.setColor(firstFont.getColor());
		f3.setStyle(firstFont.getStyle());
		PdfFontSelector selector = new PdfFontSelector();
		selector.addFont(firstFont);
		selector.addFont(f2);
		selector.addFont(f3);
		
		// 页眉是3个格子, 页脚也是3个格子, 且只处理文本内容(Excel可以设置图片等其他内容)
		float maxPHFWidth = doc.getPageSize().getWidth() - doc.leftMargin() - doc.rightMargin();

		List<PdfTableCell> header = new ArrayList<PdfTableCell>();
		List<PdfTableCell> footer = new ArrayList<PdfTableCell>();
		
		PdfPHFEvent event = new PdfPHFEvent();
		event.fontSelector = selector;
		event.headerHeight = headerHeight;
		event.maxPHFWidth = maxPHFWidth;
		event.pageHRelativeWidths = new float[]{ maxPHFWidth * 0.33f, maxPHFWidth * 0.34f, maxPHFWidth * 0.33f };
		event.pageFRelativeWidths = new float[]{ maxPHFWidth * 0.33f, maxPHFWidth * 0.34f, maxPHFWidth * 0.33f };
		
		// 变量值&D,&T,&F,&A
		Date now = new Date();
		event.dateConstString = new SimpleDateFormat("yyyy-MM-dd").format(now);
		event.timeConstString = new SimpleDateFormat("HH:mm:ss").format(now);
		event.fileNameConstString = sheet.getSheetName();
		event.sheetNameConstString = sheet.getSheetName();
		
		// 页眉左边部分
		String reportName = report.getSheetMapping().get(Const.MAIN);
		Header pageHeader = sheet.getHeader();	
		String hleft = pageHeader.getLeft();
		try {
			PdfTableCell cell = getPHFCell(event, hleft, 1, 1, headerHeight, Rectangle.ALIGN_LEFT, Rectangle.ALIGN_TOP);
			header.add(cell);
		} catch (Exception e) {
			throw new IllegalArgumentException("无法预处理sheet[" + reportName + "]左侧页眉：" + hleft, e);
		}
		
		// 页眉中间部分
		String hcenter = pageHeader.getCenter();
		try {
			PdfTableCell cell = getPHFCell(event, hcenter, 1, 1, headerHeight, Rectangle.ALIGN_CENTER, Rectangle.ALIGN_TOP);
			header.add(cell);
		} catch (Exception e) {
			throw new IllegalArgumentException("无法预处理sheet[" + reportName + "]中间页眉："+ hcenter, e);
		}
		
		// 页眉右边部分
		String hright = pageHeader.getRight();
		try {
			PdfTableCell cell = getPHFCell(event, hright, 1, 1, headerHeight, Rectangle.ALIGN_RIGHT, Rectangle.ALIGN_TOP);
			header.add(cell);
		} catch (Exception e) {
			throw new IllegalArgumentException("无法预处理sheet[" + reportName + "]右侧页眉："+ hright, e);
		}
		
		// 页脚左边部分
		Footer pageFooter = sheet.getFooter();
		String fleft = pageFooter.getLeft();
		try {
			PdfTableCell cell = getPHFCell(event, fleft, 1, 1, footerHeight, Rectangle.ALIGN_LEFT, Rectangle.ALIGN_TOP);
			footer.add(cell);
		} catch (Exception e) {
			throw new IllegalArgumentException("无法预处理sheet[" + reportName + "]左侧页脚：" + fleft, e);
		}
		
		// 页脚中间部分
		String fcenter = pageFooter.getCenter();
		try {
			PdfTableCell cell = getPHFCell(event, fcenter, 1, 1, footerHeight, Rectangle.ALIGN_CENTER, Rectangle.ALIGN_TOP);
			footer.add(cell);
		} catch (Exception e) {
			throw new IllegalArgumentException("无法预处理sheet[" + reportName + "]中间页脚："+ fcenter, e);
		}
		
		// 页脚右边部分
		String fright = pageFooter.getRight();
		try {
			PdfTableCell cell = getPHFCell(event, fright, 1, 1, footerHeight, Rectangle.ALIGN_RIGHT, Rectangle.ALIGN_TOP);
			footer.add(cell);
		} catch (Exception e) {
			throw new IllegalArgumentException("无法预处理sheet[" + reportName + "]右侧页脚："+ fright, e);
		}

		event.header = header;
		event.footer = footer;

		return event;
	}
	
	private static boolean hasPHF(Report report, Sheet sheet) {
		PageCell page = report.getPage(report.getSheetMapping().get(Const.MAIN));
		
		if (page.isIncludePHF()) {
			return true;
		}
		
		// sheet获得的不处理,只判断传入的参数. hasPHF(sheet.getHeader())
		
		return false;
	}

	private static PdfTableCell getPHFCell(PdfPHFEvent event, String text, int rowspan, int colspan, float height, int ha, int va) throws Exception {

		text = event.renderConstString(text);
		
		PdfTableCell cell = getPHFCell(text, rowspan, colspan, height, ha, va);
		
		boolean _hasPageNumber = PdfPHFEvent.hasPageNubmer(text);
		boolean _hasPageTotalNumber = PdfPHFEvent.hasPageTotalNubmer(text);
		
		if (_hasPageNumber) {
			cell.putAttr(PdfPHFEvent.P, Boolean.TRUE);
			
			if (!event.hasPageNubmer) {
				event.hasPageNubmer = _hasPageNumber;
			}
		}
		if (_hasPageTotalNumber) {
			cell.putAttr(PdfPHFEvent.N, Boolean.TRUE);
			
			if (!event.hasPageTotalNumber) {
				event.hasPageTotalNumber = _hasPageTotalNumber;
			}
		}
		
		return cell;
	}
	
	private static PdfTableCell getPHFCell(String text, int rowspan, int colspan, float height, int ha, int va) {
		PdfTableCell cell = new PdfTableCell();
		
		if (text != null && text.length() > 0) {
			cell.setAndSavePhrase(new Phrase(text));
		} else {
			Phrase phrase = new Phrase("");
			cell.setAndSavePhrase(phrase);
		}

		cell.setBorder(Rectangle.NO_BORDER);
		cell.setHorizontalAlignment(ha);
		cell.setVerticalAlignment(va);
		cell.setRowspan(rowspan);
		cell.setColspan(colspan);
		cell.setFixedHeight(height);
		
		return cell;
	}
	
	/**
	 * 通过Excel中Sheet的打印设置获得PDF的文档大小.<br/>
	 * printSetup.getPaperSize()可以获得更多的值.<br/>
	 * https://msdn.microsoft.com/zh-cn/library/ms226507 <br/>
	 * http://poi.apache.org/apidocs/org/apache/poi/ss/usermodel/PrintSetup.html <br/>
	 * http://poi.apache.org/apidocs/org/apache/poi/xssf/usermodel/XSSFSheet.html <br/>
	 * 
	 * @param sheet
	 * @return
	 */
	private static Document getPdfDocumentBySheetPrintSetup(Sheet sheet) {
		PrintSetup printSetup = sheet.getPrintSetup();
		PaperSize paperSize = null;
		if(printSetup instanceof XSSFPrintSetup){
			paperSize = ((XSSFPrintSetup)printSetup).getPaperSizeEnum();
		}else if(printSetup instanceof HSSFPrintSetup){
			paperSize = PaperSize.values()[printSetup.getPaperSize() - 1];
		}
		
		Rectangle pageSize = null;
		if (paperSize == null) {
			pageSize = PageSize.A4;
		} else {
			switch (paperSize) {
				case LETTER_PAPER : {
					pageSize = PageSize.LETTER;
				    break;
				}
				case LETTER_SMALL_PAPER : {
					pageSize = PageSize.LETTER;
				    break;
				}
				case TABLOID_PAPER : {
					pageSize = PageSize.TABLOID;
				    break;
				}
				case LEDGER_PAPER : {
					pageSize = PageSize.LEDGER;
				    break;
				}
				case LEGAL_PAPER : {
					pageSize = PageSize.LEGAL;
				    break;
				}
				case STATEMENT_PAPER : {
					pageSize = new RectangleReadOnly(396, 612);
				    break;
				}
				case EXECUTIVE_PAPER : {
					pageSize = new RectangleReadOnly(522, 756);
				    break;
				}
				case A3_PAPER : {
					pageSize = PageSize.A3;
				    break;
				}
				case A4_PAPER : {
					pageSize = PageSize.A4;
				    break;
				}
				case A4_SMALL_PAPER : {
					pageSize = PageSize.A4;
				    break;
				}
				case A5_PAPER : {
					pageSize = PageSize.A5;
				    break;
				}
				case B4_PAPER : {
					pageSize = PageSize.B4;
				    break;
				}
				case B5_PAPER : {
					pageSize = PageSize.B5;
				    break;
				}
				case FOLIO_PAPER : {
					pageSize = new RectangleReadOnly(612, 936);
				    break;
				}
				case QUARTO_PAPER : {
					pageSize = new RectangleReadOnly(610, 780);
				    break;
				}
				case STANDARD_PAPER_10_14 : {
					pageSize = new RectangleReadOnly(720, 1008);
				    break;
				}
				case STANDARD_PAPER_11_17 : {
					pageSize = PageSize._11X17;
					break;
				} 
				default : {
					pageSize = PageSize.A4;
					break;
				}
			}
		}
		
		// Gets the size of the margin in inches. Needs covert to Pt.
		float fac = UNIT_INCH_TO_PT_FAC;
		float marginLeft = (float) (sheet.getMargin(XSSFSheet.LeftMargin) * fac);
		float marginRight = (float) (sheet.getMargin(XSSFSheet.RightMargin) * fac);
		float marginTop = (float) (sheet.getMargin(XSSFSheet.TopMargin) * fac);
		float marginBottom = (float) (sheet.getMargin(XSSFSheet.BottomMargin) * fac);
		
		// 横向/纵向  - pageSize.rotate();
		pageSize = printSetup.getLandscape() ? new RectangleReadOnly(pageSize.getHeight(), pageSize.getWidth()) : pageSize;

		Document doc = new Document(pageSize, marginLeft, marginRight, marginTop, marginBottom);
		
		return doc;
	}
	
	/**
	 * <code>PdfExcelTable</code>绘制Pdf的具体实现类.
	 */
	private static class ReportExcelPdfTable {

		private Report report;
		private ExcelWorkbook workbook;
		private PdfWriter writer;
		private Document document;
		
		public ReportExcelPdfTable(Report report, ExcelWorkbook workbook, PdfWriter writer, Document document) {
			this.report = report;
			this.workbook = workbook;
			this.writer = writer;
			this.document = document;
		}
		
		/**
		 * 按照report绘制pdf表格. <br/>
		 * <blockquote>
		 * 
		 * <pre>
		 * HTML绘制标签顺序: css -> js -> &lt;table&gt; -> title.tr -> header.tr -> detail.tr -> footer.tr -> floatings ==> 最后结构 CSS + TABLE + floatings + JS
		 * PDF绘制表格顺序:  title -> header -> detail -> footer -> floatings ==> 最后结构 TABLE + floatings
		 * </pre>
		 * 
		 * </blockquote>
		 * 
		 * @param strategy
		 * @return
		 * @throws Exception
		 */
		public void drawPdf(DrawStrategy strategy) throws Exception {
			
			Rectangle pageSize = document.getPageSize();
			float onePageFitWidth = toRoundHalfUpFloat(pageSize.getWidth() - document.leftMargin() - document.rightMargin(), 2);
			float onePageFitHeight = toRoundHalfUpFloat(pageSize.getHeight() - document.topMargin() - document.bottomMargin(), 2);

			if (onePageFitWidth < 1 || onePageFitHeight < 1) {
				throw new IllegalArgumentException("Page size error - width:" + onePageFitWidth + "  height:" + onePageFitHeight);
			}
			
			strategy.setReportExcelPdfTable(this);
			strategy.setDrawBodyWidth(onePageFitWidth);
			strategy.setDrawBodyHeight(onePageFitHeight);
			
			PdfTableCellInfo tableCellInfo = getPdfTableCellInfo(strategy);
			
			draw(writer, document, tableCellInfo);
		}
		
		/**
		 * 根据策略获得要绘制的格子信息
		 * 
		 * @param strategy
		 * @return
		 * @throws Exception
		 */
		public PdfTableCellInfo getPdfTableCellInfo(DrawStrategy strategy) throws Exception {
			
			float onePageFitWidth = strategy.getDrawBodyWidth();
			
			Report report = this.report;

			ExcelWorkbook excel = this.workbook;
			
			String reportName = report.getSheetMapping().get(Const.MAIN);

			Sheet sheet = excel.getSheet(reportName);

			Set<ResultCell> visited = new HashSet<ResultCell>();
			int rowCount = report.getRowCount(reportName);
			int colCount = report.getColCount(reportName);
			
			PageCell pageCell = report.getPage(reportName);
			float pageCellFontWidth = pageCell.getFontWidth();
			
			float zoom = UNIT_PX_TO_PT_FAC; // 1.0f;
			float imageZoom = zoom;
			float fontZoom = 0f;
			float totalWidth = 0f;
			float[] relativeWidths = new float[colCount];
			for (int col = 0; col < colCount; col++) {
				float width = getColPixelWidth(sheet, col, pageCellFontWidth, zoom);
				relativeWidths[col] = width;
				totalWidth += width;
			}
			
			// 如果内容宽度大于pageSize宽度, 则缩放到pageSize
			totalWidth = toRoundHalfUpFloat(totalWidth, 2);
			boolean isDynamicZoom = pageCell.isDynamicZoom();
			if (isDynamicZoom && totalWidth != onePageFitWidth) {
				float fac = onePageFitWidth / totalWidth;
				for (int col = 0; col < colCount; col++) {
					relativeWidths[col] = relativeWidths[col] * fac - 0.001f;
				}
				zoom = fac * zoom;
				fontZoom = fac;
				imageZoom = zoom;
			}
			
			List<PdfTableCell> title  = new ArrayList<PdfTableCell>();
            List<PdfTableCell> header = new ArrayList<PdfTableCell>();
            List<PdfTableCell> pdfCells = new ArrayList<PdfTableCell>();
            List<PdfTableCell> footer = new ArrayList<PdfTableCell>();
            List<PdfTableCell> floating = new ArrayList<PdfTableCell>();
            
            // Excel关键信息
            ExcelCellInfo excelCellInfo = new ExcelCellInfo();
            excelCellInfo.report = report;
            excelCellInfo.sheet = sheet;
            excelCellInfo.cellImageList = new ArrayList<PdfTableCell>();
            excelCellInfo.excelCellImageList = new ArrayList<ChartResultCell>();
            excelCellInfo.floatingList = new ArrayList<PdfTableCell>();
            excelCellInfo.excelFloatingList = new ArrayList<FloatingResultCell>();

			// title-------------------------------------------------------------------------------------
			if (pageCell.getTitleCount() > 0) {
    			addPdfCell(title, report, excel, sheet, visited, 
    			           0, 
    			           pageCell.getTitleCount(), 
    			           zoom, fontZoom, excelCellInfo);
			}

			// header------------------------------------------------------------------------------------
			if (pageCell.getHeaderCount() > 0) {
    			addPdfCell(header, report, excel, sheet, visited, 
    			           pageCell.getTitleCount(), 
    			           pageCell.getTitleCount() + pageCell.getHeaderCount(), 
    			           zoom, fontZoom, excelCellInfo);
			}

			// detail------------------------------------------------------------------------------------
			if (rowCount - pageCell.getFooterCount() > 0) {
    			addPdfCell(pdfCells, report, excel, sheet, visited, 
    			           pageCell.getTitleCount() + pageCell.getHeaderCount(), 
    			           rowCount - pageCell.getFooterCount(), 
    			           zoom, fontZoom, excelCellInfo);
			}

			// footer------------------------------------------------------------------------------------
			if (pageCell.getFooterCount() > 0) {
    			addPdfCell(footer, report, excel, sheet, visited, 
    			           rowCount - pageCell.getFooterCount(), 
    			           rowCount, 
    			           zoom, fontZoom, excelCellInfo);
			}

			// floatings---------------------------------------------------------------------------------
			Set<ResultCell> cells = report.getImages(reportName).keySet();
			// Date: 2017-06-16 - 高度不缩放, 图片大小不改变
			imageZoom = UNIT_PX_TO_PT_FAC;
			for(ResultCell rc: cells){
				if(rc instanceof FloatingResultCell){
					float top = 0;
					for(int r = 0; r < rc.getRbegin(); r++){
						top += (getRowPixelHeight(sheet, r, UNIT_PX_TO_PT_FAC/*zoom*/));
					}
					float left = 0;
					for(int c = 0; c < rc.getCbegin(); c++){
						left += (getColPixelWidth(sheet, c, pageCellFontWidth, zoom));
					}
					top = toRoundHalfUpFloat(top, 2);
					left = toRoundHalfUpFloat(left, 2);
					
					PdfTableCell pdfPCell = new PdfTableCell();
					pdfPCell.rowIndex = rc.getRbegin();
					pdfPCell.colIndex = rc.getCbegin();
					pdfPCell.setRowspan(rc.getRowInc());
					pdfPCell.setColspan(rc.getColInc());
					pdfPCell.setBorder(Rectangle.NO_BORDER);

					// 判断是否重新生成图片
					Image img = null;
					if (zoom == UNIT_PX_TO_PT_FAC) {
						img = getImage(report, rc);
					} else {
						float widthPx = 0;
						for (int col = rc.getCbegin(), cEnd = rc.getCend() + 1; col < cEnd; ++col) {
							widthPx += relativeWidths[col];
						}
						
						float heightPx = getRowPixelHeight(sheet, pdfPCell.rowIndex, UNIT_PX_TO_PT_FAC);
						int rowspanNum = pdfPCell.getRowspan();
						if (rowspanNum > 1) {
							for (int i = 1, j = rc.getRbegin() + 1; i < rowspanNum; ++i, ++j) {
								heightPx += getRowPixelHeight(sheet, j, UNIT_PX_TO_PT_FAC);
							}
						}
						
						widthPx = (widthPx / UNIT_PX_TO_PT_FAC);
						heightPx = (heightPx / UNIT_PX_TO_PT_FAC);
						
						img = getImage(report, (FloatingResultCell) rc, (int) (widthPx + 0.1f), (int) (heightPx + 0.1f));
					}
					pdfPCell.setImage(img);
					
					pdfPCell.maxWidth = img.getScaledWidth() * imageZoom/*zoom*/;
					pdfPCell.setFixedHeight(img.getScaledHeight() * imageZoom/*zoom*/);
					pdfPCell.xPos = left;
					pdfPCell.yPos = top;
					floating.add(pdfPCell);
					
					excelCellInfo.floatingList.add(pdfPCell);
					excelCellInfo.excelFloatingList.add((FloatingResultCell) rc);
				}
			}
			
			PdfTableCellInfo tableCellInfo = new PdfTableCellInfo(title, header, pdfCells, footer, floating, relativeWidths, excelCellInfo, strategy);
			
			tableCellInfo.cellZoom = zoom;
			tableCellInfo.imageZoom = imageZoom;
			tableCellInfo.fontZoom = fontZoom;
			
			tableCellInfo.colCount = colCount;
			tableCellInfo.rowCount = rowCount;
			
			return tableCellInfo;
		}

		private void addPdfCell(List<PdfTableCell> pdfCells, Report report, ExcelWorkbook excel, Sheet sheet,  
		                        Set<ResultCell> visited, int rowStartIndex, int rowEndIndex, float zoom, float fontZoom,
		                        ExcelCellInfo excelCellInfo) throws Exception {
			
			Map<Integer, List<PdfTableCell>> rowCellMapping = null;
			Set<Integer> rowCellRowIndexSet = null;
			String reportName = report.getSheetMapping().get(Const.MAIN);
			PageCell pageCell = report.getPage(reportName);
			if (pageCell.isDynamicSize()) {
				rowCellMapping = new HashMap<Integer, List<PdfTableCell>>();
				rowCellRowIndexSet = new HashSet<Integer>();
			}
			
			int colCount = report.getColCount(reportName);
			
			for (int rowIndex = rowStartIndex; rowIndex < rowEndIndex; rowIndex++) {

				for (int colIndex = 0; colIndex < colCount; colIndex++) {
					PdfTableCell pdfPCell = null;

					ResultCell cell = report.getCell(reportName,rowIndex, colIndex);
					int rowspan = 1;
					int colspan = 1;
					if (cell == null) {
						pdfPCell = new PdfTableCell();
						pdfCells.add(pdfPCell);
						pdfPCell.setRowspan(rowspan);
						pdfPCell.setColspan(colspan);
						
						pdfPCell.setBorder(Rectangle.NO_BORDER);
						
						pdfPCell.rowIndex = rowIndex;
						pdfPCell.colIndex = colIndex;
					} else if (!visited.contains(cell)) {
						visited.add(cell);

						pdfPCell = new PdfTableCell();
						pdfCells.add(pdfPCell);
						rowspan = cell.getRowInc();
						colspan = cell.getColInc();
						pdfPCell.setRowspan(rowspan);
						pdfPCell.setColspan(colspan);
						
						pdfPCell.rowIndex = rowIndex;
						pdfPCell.colIndex = colIndex;
						
						Cell excelCell = sheet.getRow(rowIndex).getCell(colIndex);
						// generateCellPreviewAttrs
						
						generateCellPreviewContent(report, cell, excelCell, zoom, fontZoom, excel, sheet, pdfPCell);
					}

					if (pdfPCell != null) {
						//if (!report.isRowAutoSize(rowIndex)) {  // PDF不自动适应行高
						// Date: 2017-06-16 - 高度不缩放
							float fixedHeight = getRowPixelHeight(sheet, rowIndex, UNIT_PX_TO_PT_FAC/*zoom*/);
							int rowspanNum = pdfPCell.getRowspan();
							if (rowspanNum > 1) {
								for (int i = 1, j = rowIndex + 1; i < rowspanNum; i++, j++) {
									fixedHeight += getRowPixelHeight(sheet, j, UNIT_PX_TO_PT_FAC/*zoom*/);
								}
							}
							pdfPCell.setFixedHeight(fixedHeight);
						//}
							
						// autoSize
						if (rowCellMapping != null) {
							Integer key = Integer.valueOf(rowIndex);
    						List<PdfTableCell> rowCellList = rowCellMapping.get(key);
    						if (rowCellList == null) {
    							rowCellList = new ArrayList<PdfTableCell>();
    							rowCellMapping.put(Integer.valueOf(rowIndex), rowCellList);
    						}
    						rowCellList.add(pdfPCell);
    						if (cell != null && !rowCellRowIndexSet.contains(key) && cell.isRowAutoSize(rowIndex, pageCell)) {
    							rowCellRowIndexSet.add(key);
    						}
						}
						
						// 格子内图片
						if (pdfPCell.getImage() != null) {
							excelCellInfo.cellImageList.add(pdfPCell);
							excelCellInfo.excelCellImageList.add((ChartResultCell) cell);
						}
					}
				}
			}
			
			// autoSize
			if (rowCellMapping != null) {
				for (Iterator<Integer> iter = rowCellRowIndexSet.iterator(); iter.hasNext(); ) {
					List<PdfTableCell> rowCellList = rowCellMapping.get(iter.next());
					for (PdfTableCell xcell : rowCellList) {
						xcell.textControl = TextControlEnum.AUTOSIZE;
					}
				}
			}
		}
		
		/**
		 * @see com.yss.sofa.report.engine.Engine#generateCellPreviewContent
		 * @param report
		 * @param cell
		 * @param excelCell
		 * @param evaluator
		 * @param zoom
		 * @param fontZoom
		 * @param excel
		 * @param sheet
		 * @param pdfPCell
		 * @throws Exception
		 */
		private void generateCellPreviewContent(Report report, ResultCell cell, Cell excelCell, float zoom, float fontZoom,
		                                        ExcelWorkbook excel, Sheet sheet, PdfTableCell pdfPCell) throws Exception {
			// generateCellPreviewContent - PDF只需要处理图像和普通的格子
			/*
			if(cell instanceof ChartResultCell){
				
			} else if (cell instanceof FormResultCell) {
				
			} else if (report.getPage().isDynamicHTML()) {
				
			} else {
				if(cell.isEscape(report.getPage())) {
					
				} else {
					
				}
			}*/
			
			// CSS - background,font,align,border
			CellStyle cellStyle = excelCell.getCellStyle();

			if(cell instanceof ChartResultCell){
				Image img = getImage(report, cell);
				pdfPCell.setImage(img);
				
				pdfPCell.textControl = TextControlEnum.CLIP;
				pdfPCell.textDrawControl = TextControlEnum.DIRECT;
			} else {
				String text = excel.getExcelCellFormattedValue(cell, excelCell);
				if(cellStyle instanceof XSSFCellStyle){
					XSSFFont font = ((XSSFCellStyle)cellStyle).getFont();
					setTextByExcel(pdfPCell, text, font, fontZoom);
					
					pdfPCell.setHorizontalAlignment(getHAlignByExcel(cellStyle));
					pdfPCell.setVerticalAlignment(getVAlignByExcel(cellStyle));
					
					setTextControlByExcel(pdfPCell,cellStyle);
				}else if(cellStyle instanceof HSSFCellStyle && sheet instanceof HSSFSheet){
					HSSFFont font = ((HSSFCellStyle)cellStyle).getFont(sheet.getWorkbook());
					setTextByExcel03(pdfPCell, text, font,(HSSFSheet)sheet, fontZoom);
					
					pdfPCell.setHorizontalAlignment(getHAlignByExcel(cellStyle));
					pdfPCell.setVerticalAlignment(getVAlignByExcel(cellStyle));
					
					setTextControlByExcel(pdfPCell,cellStyle);
				}
			}
			if(cellStyle instanceof XSSFCellStyle && sheet instanceof XSSFSheet){
				pdfPCell.setBackgroundColor(getBackgroundColorByExcel((XSSFCellStyle)cellStyle));
				setBorderByExcel(pdfPCell, (XSSFSheet)sheet, (XSSFCellStyle)cellStyle);
			}else if(cellStyle instanceof HSSFCellStyle && sheet instanceof HSSFSheet){
				pdfPCell.setBackgroundColor(getBackgroundColorByExcel03((HSSFCellStyle)cellStyle));
				setBorderByExcel03(pdfPCell, (HSSFSheet)sheet, (HSSFCellStyle)cellStyle);
			}
		}
		
		/**
		 * 获得格子中的图形
		 * @param report
		 * @param cell
		 * @return
		 * @throws Exception
		 */
		public Image getImage(Report report, ResultCell cell) throws Exception {
			byte[] imgb = report.getImages(Const.MAIN).get(cell); //ImageUtil.zoom(report.getImages().get(cell), 1.0f); // 使用Thumb压缩会失真
			Image img = Image.getInstance(imgb);
			PdfImage pdfImage = new PdfImage(img, imgb);
			return pdfImage;
		}
		
		/**
		 * 重新获取图片
		 * @param report
		 * @param rc
		 * @param widthPx
		 * @param heightPx
		 * @return
		 * @throws Exception
		 */
		public Image getImage(Report report, FloatingResultCell rc, int widthPx, int heightPx) throws Exception {
			byte[] imgb = com.yss.sofa.report.engine.util.ChartUtil.generateFloatingPNG((FloatingResultCell) rc, 
			                                                                                widthPx, 
			                                                                                heightPx,
								                                                            report.getParams(),
								                                                            report.getConsts());
			Image img = Image.getInstance(imgb);
			PdfImage pdfImage = new PdfImage(img, imgb);
			return pdfImage;
		}
		
		/**
		 * 计算颜色的RGB值
		 * @param color
		 * @return
		 */
		public static int getRGB(XSSFColor color) {

			int result = 0x00000000; //0x00FFFFFF; ARGB

			if (color == null) {
				return result;
			}

			int red = 0;
			int green = 0;
			int blue = 0;

			XSSFColor xssfColor = (XSSFColor) color;
			byte[] rgb = xssfColor.getRGBWithTint();
			if (rgb == null) {
				return result;
			}
			red = (rgb[0] < 0) ? (rgb[0] + 256) : rgb[0];
			green = (rgb[1] < 0) ? (rgb[1] + 256) : rgb[1];
			blue = (rgb[2] < 0) ? (rgb[2] + 256) : rgb[2];

			if (red != 0 || green != 0 || blue != 0) {
				//result = new java.awt.Color(red, green, blue).getRGB();
				result = ((255 & 0xFF) << 24) |
		                ((red & 0xFF) << 16) |
		                ((green & 0xFF) << 8)  |
		                ((blue & 0xFF) << 0);
			}

			return result;
		}

		/**
		 * 计算颜色的RGB值
		 * @param color
		 * @return
		 */
		public static int getRGB(HSSFColor color) {

			int result = 0x00000000; //0x00FFFFFF; ARGB

			if (color == null) {
				return result;
			}
			int red = 0;
			int green = 0;
			int blue = 0;
			
			HSSFColor hssfColor = (HSSFColor) color;
			short[] rgb = hssfColor.getTriplet();
			if (rgb == null) {
				return result;
			}
			red = (rgb[0] < 0) ? (rgb[0] + 256) : rgb[0];
			green = (rgb[1] < 0) ? (rgb[1] + 256) : rgb[1];
			blue = (rgb[2] < 0) ? (rgb[2] + 256) : rgb[2];

			if (red != 0 || green != 0 || blue != 0) {
				//result = new java.awt.Color(red, green, blue).getRGB();
				result = ((255 & 0xFF) << 24) |
		                ((red & 0xFF) << 16) |
		                ((green & 0xFF) << 8)  |
		                ((blue & 0xFF) << 0);
			}
			return result;
		}
		
		/**
		 * 过长或过大的文本内容是否进行自动换行,缩放填充,自动截取.<br/>
		 * 1. 自动换行(设置单元格格式-对齐-文本控制). 自动换行并固定高度不处理(Excel设置为一个实心点, 可以设置自动换行后改变格子高度)<br/>
		 * 2. 缩小填充(设置单元格格式-对齐-文本控制)。只有文本宽度大于格子宽度才会缩放到适合格子大小<br/>
		 * 3. 截断
		 * @param pdfPCell
		 * @param style
		 * @see com.yss.sofa.report.engine.util.PrintUtil toPrintTextStretch
		 */
		private void setTextControlByExcel(PdfTableCell pdfPCell, CellStyle style) {
			if (style.getWrapText()) {
				pdfPCell.textControl = TextControlEnum.STRETCH;
			} else if (style.getShrinkToFit()) {
				pdfPCell.textControl = TextControlEnum.SHRINK;
			} else {
				pdfPCell.textControl = TextControlEnum.CLIP;
			}
		}
		
		/**
		 * 设置Excel对应的PDF文本内容
		 * @param pdfPCell
		 * @param text
		 * @param font
		 * @param fontZoom
		 */
		private void setTextByExcel(PdfTableCell pdfPCell, String text, XSSFFont font, float fontZoom) {
			
			Font firstFont = getFontByExcel(font);
			
			// 页面放大字体大小不变, 缩小要变化
			// $Date: 2017-06-16 - 缩小也不变
			//if (fontZoom > 0 && fontZoom < 1) {
			//	firstFont.setSize(firstFont.getSize() * fontZoom);
			//}
			
			PdfFontSelector selector = new PdfFontSelector();
			
			if (text == null || text.trim().length() == 0) {
				selector.addFont(firstFont);
			} else {	
				float firstFontSize = firstFont.getSize();
				
				if (!FONT_STSONG.equals(firstFont.getBaseFont().getPostscriptFontName())) {
					Font f2 = FontFactory.getFont(FONT_STSONG, FONT_ENCODING_UNIGB_UCS2_H, BaseFont.NOT_EMBEDDED, firstFontSize); // 简体宋体
					Font f3 = FontFactory.getFont(FONT_MSUNG, FONT_ENCODING_UNICNS_UCS2_H, BaseFont.NOT_EMBEDDED, firstFontSize); // 繁体宋体
					Font f4 = FontFactory.getFont(FONT_SEGOE_UI,BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED, firstFontSize);
					
					f2.setColor(firstFont.getColor());
					f2.setStyle(firstFont.getStyle());
					f3.setColor(firstFont.getColor());
					f3.setStyle(firstFont.getStyle());
					f4.setColor(firstFont.getColor());
					f4.setStyle(firstFont.getStyle());

					selector.addFont(firstFont);
					selector.addFont(f2);
					selector.addFont(f3);
					selector.addFont(f4);
				} else {
					Font f3 = FontFactory.getFont(FONT_MSUNG, FONT_ENCODING_UNICNS_UCS2_H, BaseFont.NOT_EMBEDDED, firstFontSize);
					Font f4 = FontFactory.getFont(FONT_SEGOE_UI,BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED, firstFontSize);
					
					f3.setColor(firstFont.getColor());
					f3.setStyle(firstFont.getStyle());
					f4.setColor(firstFont.getColor());
					f4.setStyle(firstFont.getStyle());

					selector.addFont(firstFont);
					selector.addFont(f3);
					selector.addFont(f4);
				}		
			}
			
			Phrase phrase = selector.process(text); // new Phrase(text, firstFont); // 这样无法计算
			
			pdfPCell.setAndSavePhrase(phrase);
			
			pdfPCell.fontSelector = selector;
		}

		/**
		 * 设置Excel对应的PDF文本内容
		 * @param pdfPCell
		 * @param text
		 * @param font
		 * @param fontZoom
		 */
		private void setTextByExcel03(PdfTableCell pdfPCell, String text, HSSFFont font,HSSFSheet sheet, float fontZoom) {
			
			Font firstFont = getFontByExcel03(font,sheet);
			
			// 页面放大字体大小不变, 缩小要变化
			// $Date: 2017-06-16 - 缩小也不变
			//if (fontZoom > 0 && fontZoom < 1) {
			//	firstFont.setSize(firstFont.getSize() * fontZoom);
			//}
			
			PdfFontSelector selector = new PdfFontSelector();
			
			if (text == null || text.trim().length() == 0) {
				selector.addFont(firstFont);
			} else {	
				float firstFontSize = firstFont.getSize();
				
				if (!FONT_STSONG.equals(firstFont.getBaseFont().getPostscriptFontName())) {
					Font f2 = FontFactory.getFont(FONT_STSONG, FONT_ENCODING_UNIGB_UCS2_H, BaseFont.NOT_EMBEDDED, firstFontSize); // 简体宋体
					Font f3 = FontFactory.getFont(FONT_MSUNG, FONT_ENCODING_UNICNS_UCS2_H, BaseFont.NOT_EMBEDDED, firstFontSize); // 繁体宋体
					Font f4 = FontFactory.getFont(FONT_SEGOE_UI,BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED, firstFontSize);
					
					f2.setColor(firstFont.getColor());
					f2.setStyle(firstFont.getStyle());
					f3.setColor(firstFont.getColor());
					f3.setStyle(firstFont.getStyle());
					f4.setColor(firstFont.getColor());
					f4.setStyle(firstFont.getStyle());

					selector.addFont(firstFont);
					selector.addFont(f2);
					selector.addFont(f3);
					selector.addFont(f4);
				} else {
					Font f3 = FontFactory.getFont(FONT_MSUNG, FONT_ENCODING_UNICNS_UCS2_H, BaseFont.NOT_EMBEDDED, firstFontSize);
					Font f4 = FontFactory.getFont(FONT_SEGOE_UI,BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED, firstFontSize);
					
					f3.setColor(firstFont.getColor());
					f3.setStyle(firstFont.getStyle());
					f4.setColor(firstFont.getColor());
					f4.setStyle(firstFont.getStyle());
					
					selector.addFont(firstFont);
					selector.addFont(f3);
					selector.addFont(f4);
				}		
			}
			
			Phrase phrase = selector.process(text); // new Phrase(text, firstFont); // 这样无法计算
			
			pdfPCell.setAndSavePhrase(phrase);
			
			pdfPCell.fontSelector = selector;
		}
		
		/**
		 * 获得Excel对应的PDF字体及其样式
		 * @param font
		 * @return
		 */
		private static Font getFontByExcel(XSSFFont font) {
			
			int fontStyle = Font.NORMAL;

			// 加粗
			if (font.getBold()) {
				fontStyle = fontStyle | Font.BOLD;
			}

			// 斜体
			if (font.getItalic()) {
				fontStyle = fontStyle | Font.ITALIC;
			}

			// 下划线
			FontUnderline underline = FontUnderline.valueOf(font.getUnderline());
			if (underline == FontUnderline.SINGLE) { // pdf没有双下划线,上下标等字体样式(可以使用小字体加上Chunk.setTextRise()方法实现,60向上移动60,-10向下移动10)
				fontStyle = fontStyle | Font.UNDERLINE;
			}
			
			// 删除线
			if (font.getStrikeout()) {
				fontStyle = fontStyle | Font.STRIKETHRU;
			}

			Font result = getFont(font.getFontName(), true);
			
			// 颜色
			XSSFColor color = font.getXSSFColor();
			
			// 使用PenUtil获得字体
			//String fontName = result.getBaseFont().getPostscriptFontName();
			int fontSize = font.getFontHeightInPoints();
			result.setSize(fontSize);
			result.setStyle(fontStyle);
			if (color != null) {
				result.setColor(new BaseColor(toAWTColor(color).getRGB()));
	        }
			
			return result;
		}

		/**
		 * 获得Excel对应的PDF字体及其样式
		 * @param font
		 * @return
		 */
		private static Font getFontByExcel03(HSSFFont font,HSSFSheet sheet) {
			
			int fontStyle = Font.NORMAL;

			// 加粗
			if (font.getBold()) {
				fontStyle = fontStyle | Font.BOLD;
			}

			// 斜体
			if (font.getItalic()) {
				fontStyle = fontStyle | Font.ITALIC;
			}

			// 下划线
			FontUnderline underline = FontUnderline.valueOf(font.getUnderline());
			if (underline == FontUnderline.SINGLE) { // pdf没有双下划线,上下标等字体样式(可以使用小字体加上Chunk.setTextRise()方法实现,60向上移动60,-10向下移动10)
				fontStyle = fontStyle | Font.UNDERLINE;
			}
			
			// 删除线
			if (font.getStrikeout()) {
				fontStyle = fontStyle | Font.STRIKETHRU;
			}

			Font result = getFont(font.getFontName(), true);
			
			// 颜色
			HSSFPalette palette = sheet.getWorkbook().getCustomPalette();
			HSSFColor color = palette.getColor(font.getColor());
			
			// 使用PenUtil获得字体
			//String fontName = result.getBaseFont().getPostscriptFontName();
			int fontSize = font.getFontHeightInPoints();
			result.setSize(fontSize);
			result.setStyle(fontStyle);
			if (color != null) {
				result.setColor(new BaseColor(toAWTColor(color).getRGB()));
	        }
			
			return result;
		}
		
		private static final ReentrantLock FONT_REGISTERED = new ReentrantLock();
		private static boolean isFontRegistered = false;
		private static Font DEFAULT_FONT_CHINESE;
		
		private static Font getFont(String fontName, boolean register) {
			Font font = null;
			
			try {
				font = FontFactory.getFont(fontName, BaseFont.IDENTITY_H, BaseFont.EMBEDDED, Font.UNDEFINED, Font.UNDEFINED, null);
				
				if (font.getBaseFont() == null && register && !isFontRegistered) {
					throw new RuntimeException();
				}
			} catch (Exception e) {
				// The font %s doesn't have unicode support
				if (register && !isFontRegistered) {
					FONT_REGISTERED.lock();
					try {
	    				if (!isFontRegistered) {
	    					registerOSDirectories();
	    					
	    					registerCustomDirectories();
	    					
	    					isFontRegistered = true;
	    				}
	    				
	    				font = FontFactory.getFont(fontName, BaseFont.IDENTITY_H, BaseFont.EMBEDDED, Font.UNDEFINED, Font.UNDEFINED, null);
					} catch (Exception ex) {
						// Do nothing...
					} finally {
						FONT_REGISTERED.unlock();
					}
				}
			}
			
			if (font.getBaseFont() == null) {
				font = new Font(DEFAULT_FONT_CHINESE);
			}
			
			return font;
		}
		
		private static void registerCustomDirectories() {
			// from itext-asian.jar or other
			try {
				if (DEFAULT_FONT_CHINESE == null) {
					BaseFont bf = BaseFont.createFont(FONT_STSONG, FONT_ENCODING_UNIGB_UCS2_H, BaseFont.NOT_EMBEDDED);
					DEFAULT_FONT_CHINESE = new Font(bf);
				}
				FontFactory.registerDirectory(SOFAEnvionmentUtil.getSOFAHomePath() + "/Fonts", true);
			} catch (Exception e) {
			}
		}
		
		private static void registerOSDirectories(){
    		if (System.getProperty("os.name", "").toUpperCase().indexOf("WIN") > -1) {
    			// Windows
    		    String windir = System.getenv("windir");
    		    String fileseparator = System.getProperty("file.separator");
    		    if ((windir != null) && (fileseparator != null)) {
    		    	FontFactory.registerDirectory(windir + fileseparator + "fonts");
    		    }
    		} else {
    			// Linux
    		    FontFactory.registerDirectory("/usr/share/X11/fonts", true);
    		    FontFactory.registerDirectory("/usr/X/lib/X11/fonts", true);
    		    FontFactory.registerDirectory("/usr/openwin/lib/X11/fonts", true);
    		    FontFactory.registerDirectory("/usr/share/fonts", true);
    		    FontFactory.registerDirectory("/usr/X11R6/lib/X11/fonts", true);
    		    // Macintosh
    		    FontFactory.registerDirectory("/Library/Fonts");
    		    FontFactory.registerDirectory("/System/Library/Fonts");
    		}
		}

		/**
		 * 获得Excel单元格底纹颜色
		 * @param style
		 * @return
		 */
		private BaseColor getBackgroundColorByExcel(XSSFCellStyle style) {
			// 单元格格式-图案-单元格底纹
			XSSFColor color = style.getFillForegroundColorColor();
			if (color == null) {
				return null;//java.awt.Color.WHITE; // 优化提示:白色背景不画可以节省大量空间
			}

			int rgb = getRGB(color);
			
			return (rgb == -1 ? null : (rgb == 0 ? BaseColor.BLACK : new BaseColor(rgb)));
		}
		
		/**
		 * 获得Excel单元格底纹颜色
		 * @param style
		 * @return
		 */
		private BaseColor getBackgroundColorByExcel03(HSSFCellStyle style) {
			// 单元格格式-图案-单元格底纹
			HSSFColor color = style.getFillForegroundColorColor();
			if (color == null) {
				return null;//java.awt.Color.WHITE; // 优化提示:白色背景不画可以节省大量空间
			}

			int rgb = getRGB(color);
			
			return (rgb == -1 ? null : (rgb == 0 ? BaseColor.BLACK : new BaseColor(rgb)));
		}
		
		/**
		 * 获得Excel对应的PDF水平对齐方式的值.<br/>
		 * Excel 2007: 常规(数值靠右,布尔(true/false)居中,其他靠左),靠左,居中,靠右,填充,两端对齐,跨列居中,分散对齐
		 * 
		 * @see org.apache.poi.ss.usermodel.CellStyle
		 * @see com.lowagie.text.Element
		 * @param style
		 * @return
		 */
		private int getHAlignByExcel(CellStyle style) {
			int result = 0;
			switch (style.getAlignmentEnum()) {
				case LEFT : {
					result = Element.ALIGN_LEFT;
					break;
				}
				case CENTER : {
					result = Element.ALIGN_CENTER;
					break;
				}
				case RIGHT : {
					result = Element.ALIGN_RIGHT;
					break;
				}
				case JUSTIFY : {
					result = Element.ALIGN_JUSTIFIED;
					break;
				}
				default : {
					result = Element.ALIGN_LEFT;
					break;
				}
			}
			return result;
		}
		
		/**
		 * 获得Excel对应的PDF垂直对齐方式的值.<br/>
		 * Excel 2007: 靠上,居中,靠下,两端对齐,分散对齐
		 * 
		 * @see org.apache.poi.ss.usermodel.CellStyle
		 * @see com.lowagie.text.Element
		 * @param style
		 * @return
		 */
		private int getVAlignByExcel(CellStyle style) {
			int result = 0;
			switch (style.getVerticalAlignmentEnum()) {
				case TOP : {
					result = Element.ALIGN_TOP;
					break;
				}
				case CENTER : {
					result = Element.ALIGN_MIDDLE;
					break;
				}
				case BOTTOM : {
					result = Element.ALIGN_BOTTOM;
					break;
				}
				case JUSTIFY : {
					result = Element.ALIGN_BASELINE;
					break;
				}
				default : {
					result = Element.ALIGN_MIDDLE;
					break;
				}
			}
			return result;
		}
		
		/**
		 * 设置Excel对应的PDF边框样式.<br/>
		 * POI: getBorderXXX() Deprecated. 3.15 beta 2. Use getBorderXXXEnum() instead.
		 * @param pdfPCell
		 * @param sheet
		 * @param style
		 */
		private void setBorderByExcel(PdfTableCell pdfPCell, XSSFSheet sheet, XSSFCellStyle style) {
			BorderRoundRectangle cellEvent = null;
			
			XSSFCellStyle xssfCellStyle = (XSSFCellStyle) style;
			
			cellEvent = new BorderRoundRectangle();
			cellEvent.setTopBorder(toPdfBorderStyle(xssfCellStyle.getBorderTopEnum(), xssfCellStyle.getBorderColor(BorderSide.TOP)));
			cellEvent.setLeftBorder(toPdfBorderStyle(xssfCellStyle.getBorderLeftEnum(), xssfCellStyle.getBorderColor(BorderSide.LEFT)));
			
			// 合并单元格边框判断如下
			BorderStyle rbs = xssfCellStyle.getBorderRightEnum();
			if (pdfPCell.getColspan() > 1 && rbs == BorderStyle.NONE) {
				XSSFCell lastMergeCell = sheet.getRow(pdfPCell.rowIndex).getCell(pdfPCell.colIndex + pdfPCell.getColspan() - 1);
				XSSFCellStyle lastMergeCellStyle = (XSSFCellStyle) lastMergeCell.getCellStyle();
				cellEvent.setRightBorder(toPdfBorderStyle(lastMergeCellStyle.getBorderRightEnum(), lastMergeCellStyle.getBorderColor(BorderSide.RIGHT)));
			} else {
				cellEvent.setRightBorder(toPdfBorderStyle(rbs, xssfCellStyle.getBorderColor(BorderSide.RIGHT)));
			}
			
			BorderStyle bbs = xssfCellStyle.getBorderBottomEnum();
			if (pdfPCell.getRowspan() > 1 && bbs == BorderStyle.NONE) {
				XSSFCell lastMergeCell = sheet.getRow(pdfPCell.rowIndex + pdfPCell.getRowspan() - 1).getCell(pdfPCell.colIndex);
				XSSFCellStyle lastMergeCellStyle = (XSSFCellStyle) lastMergeCell.getCellStyle();
				cellEvent.setBottomBorder(toPdfBorderStyle(lastMergeCellStyle.getBorderBottomEnum(), lastMergeCellStyle.getBorderColor(BorderSide.BOTTOM)));
			} else {
				cellEvent.setBottomBorder(toPdfBorderStyle(bbs, xssfCellStyle.getBorderColor(BorderSide.BOTTOM)));
			}
			
			pdfPCell.topBorderWidth = cellEvent.getTopBorder() != null ? cellEvent.getTopBorder().getBorderWidth() : 0;
			pdfPCell.leftBorderWidth = cellEvent.getLeftBorder() != null ? cellEvent.getLeftBorder().getBorderWidth() : 0;
			pdfPCell.bottomBorderWidth = cellEvent.getBottomBorder() != null ? cellEvent.getBottomBorder().getBorderWidth() : 0;
			pdfPCell.rightBorderWidth = cellEvent.getRightBorder() != null ? cellEvent.getRightBorder().getBorderWidth() : 0;

			pdfPCell.setBorder(Rectangle.NO_BORDER);
			pdfPCell.setCellEvent(cellEvent);
		}
		
		/**
		 * 设置Excel对应的PDF边框样式.<br/>
		 * POI: getBorderXXX() Deprecated. 3.15 beta 2. Use getBorderXXXEnum() instead.
		 * @param pdfPCell
		 * @param sheet
		 * @param style
		 */
		private void setBorderByExcel03(PdfTableCell pdfPCell, HSSFSheet sheet, HSSFCellStyle style) {
			BorderRoundRectangle cellEvent = null;
			
			HSSFCellStyle hssfCellStyle = (HSSFCellStyle) style;
			
			cellEvent = new BorderRoundRectangle();
			
			HSSFPalette palette = ((HSSFWorkbook) sheet.getWorkbook()).getCustomPalette();
			
			cellEvent.setTopBorder(toPdfBorderStyle03(hssfCellStyle.getBorderTopEnum(), palette.getColor(hssfCellStyle.getTopBorderColor())));
			cellEvent.setLeftBorder(toPdfBorderStyle03(hssfCellStyle.getBorderLeftEnum(), palette.getColor(hssfCellStyle.getLeftBorderColor())));
			
			// 合并单元格边框判断如下
			BorderStyle rbs = hssfCellStyle.getBorderRightEnum();
			if (pdfPCell.getColspan() > 1 && rbs == BorderStyle.NONE) {
				HSSFCell lastMergeCell = sheet.getRow(pdfPCell.rowIndex).getCell(pdfPCell.colIndex + pdfPCell.getColspan() - 1);
				HSSFCellStyle lastMergeCellStyle = (HSSFCellStyle) lastMergeCell.getCellStyle();
				cellEvent.setRightBorder(toPdfBorderStyle03(lastMergeCellStyle.getBorderRightEnum(), palette.getColor(lastMergeCellStyle.getRightBorderColor())));
			} else {
				cellEvent.setRightBorder(toPdfBorderStyle03(rbs, palette.getColor(hssfCellStyle.getRightBorderColor())));
			}
			
			BorderStyle bbs = hssfCellStyle.getBorderBottomEnum();
			if (pdfPCell.getRowspan() > 1 && bbs == BorderStyle.NONE) {
				HSSFCell lastMergeCell = sheet.getRow(pdfPCell.rowIndex + pdfPCell.getRowspan() - 1).getCell(pdfPCell.colIndex);
				HSSFCellStyle lastMergeCellStyle = (HSSFCellStyle) lastMergeCell.getCellStyle();
				cellEvent.setBottomBorder(toPdfBorderStyle03(lastMergeCellStyle.getBorderBottomEnum(), palette.getColor(lastMergeCellStyle.getBottomBorderColor())));
			} else {
				cellEvent.setBottomBorder(toPdfBorderStyle03(bbs, palette.getColor(hssfCellStyle.getBottomBorderColor())));
			}
			
			pdfPCell.topBorderWidth = cellEvent.getTopBorder() != null ? cellEvent.getTopBorder().getBorderWidth() : 0;
			pdfPCell.leftBorderWidth = cellEvent.getLeftBorder() != null ? cellEvent.getLeftBorder().getBorderWidth() : 0;
			pdfPCell.bottomBorderWidth = cellEvent.getBottomBorder() != null ? cellEvent.getBottomBorder().getBorderWidth() : 0;
			pdfPCell.rightBorderWidth = cellEvent.getRightBorder() != null ? cellEvent.getRightBorder().getBorderWidth() : 0;

			pdfPCell.setBorder(Rectangle.NO_BORDER);
			pdfPCell.setCellEvent(cellEvent);
		}
		
		private PdfBorderStyle toPdfBorderStyle(BorderStyle border, XSSFColor color){
			PdfBorderStyle style = null;
			float width=0; //1.0f * UNIT_PT_TO_PX_FAC;
			switch (border) {
			case NONE:
				//width=1;
				break;
			case THIN:
				//width=1;
				width = 0.5f; //1 * UNIT_PT_TO_PX_FAC; //0.5f;
				style = new LineBorderStyle(width, toAWTColor(color));
				break;
			case MEDIUM:
				//width=2;
				width = 2 * UNIT_PX_TO_PT_FAC; //1.0f;
				style = new LineBorderStyle(width, toAWTColor(color));
				break;
			case DASHED:
				//width=1;
				width = 1 * UNIT_PX_TO_PT_FAC;
				style = new DashedBorderStyle(width, toAWTColor(color));
				break;
			case DOTTED:
				//width=1;
				width = 1 * UNIT_PX_TO_PT_FAC;
				style = new DottedBorderStyle(width, toAWTColor(color));
				break;
			case THICK:
				//width=3;
				width = 3 * UNIT_PX_TO_PT_FAC; //1.5f;
				style = new LineBorderStyle(width, toAWTColor(color));
				break;
			case DOUBLE:
				//width=3;
				width = 0.4f;
				style = new DoubleBorderStyle(width, toAWTColor(color));
				break;
			case HAIR:
				//width=1;
				width = 1 * UNIT_PX_TO_PT_FAC;
				style = new DottedBorderStyle(width, toAWTColor(color));
				break;
			case MEDIUM_DASHED:
				//width=2;
				width = 2 * UNIT_PX_TO_PT_FAC; //1.5f;
				style = new DashedBorderStyle(width, toAWTColor(color));
				break;
			case DASH_DOT:
				//width=1;
				width = 1 * UNIT_PX_TO_PT_FAC;
				style = new DashDotBorderStyle(width, toAWTColor(color));
				break;
			case MEDIUM_DASH_DOT:
				//width=2;
				width = 2 * UNIT_PX_TO_PT_FAC; //1.5f;
				style = new DashDotBorderStyle(width, toAWTColor(color));
				break;
			case DASH_DOT_DOT:
				//width=1;
				width = 1 * UNIT_PX_TO_PT_FAC;
				style = new DashDotBorderStyle(width, toAWTColor(color));
				break;
			case MEDIUM_DASH_DOT_DOT:
				//width=2;
				width = 2 * UNIT_PX_TO_PT_FAC; //1.5f;
				style = new DashDotBorderStyle(width, toAWTColor(color));
				break;
			case SLANTED_DASH_DOT:
				//width=2;
				width = 2 * UNIT_PX_TO_PT_FAC; //1.5f;
				style = new DashDotBorderStyle(width, toAWTColor(color));
				break;
			default:
				break;
			}
			// pdf默认边框宽度为0.5pt, excel默认边框宽度为1px
			return style;
		}
		
		private PdfBorderStyle toPdfBorderStyle03(BorderStyle border, HSSFColor color){
			PdfBorderStyle style = null;
			float width=0; //1.0f * UNIT_PT_TO_PX_FAC;
			switch (border) {
			case NONE:
				//width=1;
				break;
			case THIN:
				//width=1;
				width = 0.5f; //1 * UNIT_PT_TO_PX_FAC; //0.5f;
				style = new LineBorderStyle(width, toAWTColor(color));
				break;
			case MEDIUM:
				//width=2;
				width = 2 * UNIT_PX_TO_PT_FAC; //1.0f;
				style = new LineBorderStyle(width, toAWTColor(color));
				break;
			case DASHED:
				//width=1;
				width = 1 * UNIT_PX_TO_PT_FAC;
				style = new DashedBorderStyle(width, toAWTColor(color));
				break;
			case DOTTED:
				//width=1;
				width = 1 * UNIT_PX_TO_PT_FAC;
				style = new DottedBorderStyle(width, toAWTColor(color));
				break;
			case THICK:
				//width=3;
				width = 3 * UNIT_PX_TO_PT_FAC; //1.5f;
				style = new LineBorderStyle(width, toAWTColor(color));
				break;
			case DOUBLE:
				//width=3;
				width = 0.4f;
				style = new DoubleBorderStyle(width, toAWTColor(color));
				break;
			case HAIR:
				//width=1;
				width = 1 * UNIT_PX_TO_PT_FAC;
				style = new DottedBorderStyle(width, toAWTColor(color));
				break;
			case MEDIUM_DASHED:
				//width=2;
				width = 2 * UNIT_PX_TO_PT_FAC; //1.5f;
				style = new DashedBorderStyle(width, toAWTColor(color));
				break;
			case DASH_DOT:
				//width=1;
				width = 1 * UNIT_PX_TO_PT_FAC;
				style = new DashDotBorderStyle(width, toAWTColor(color));
				break;
			case MEDIUM_DASH_DOT:
				//width=2;
				width = 2 * UNIT_PX_TO_PT_FAC; //1.5f;
				style = new DashDotBorderStyle(width, toAWTColor(color));
				break;
			case DASH_DOT_DOT:
				//width=1;
				width = 1 * UNIT_PX_TO_PT_FAC;
				style = new DashDotBorderStyle(width, toAWTColor(color));
				break;
			case MEDIUM_DASH_DOT_DOT:
				//width=2;
				width = 2 * UNIT_PX_TO_PT_FAC; //1.5f;
				style = new DashDotBorderStyle(width, toAWTColor(color));
				break;
			case SLANTED_DASH_DOT:
				//width=2;
				width = 2 * UNIT_PX_TO_PT_FAC; //1.5f;
				style = new DashDotBorderStyle(width, toAWTColor(color));
				break;
			default:
				break;
			}
			// pdf默认边框宽度为0.5pt, excel默认边框宽度为1px
			return style;
		}
		
		
		private static java.awt.Color toAWTColor(XSSFColor color) {
			return new java.awt.Color(getRGB(color));
		}

		private static java.awt.Color toAWTColor(HSSFColor color) {
			return new java.awt.Color(getRGB(color));
		}
		
		/**
		 * @see com.yss.sofa.report.engine#getColPixelWidth
		 * @param sheet
		 * @param col
		 * @param fontWidth
		 * @param zoom
		 * @return
		 */
		private float getColPixelWidth(Sheet sheet, int col, float fontWidth, float zoom) {

			if (sheet.isColumnHidden(col)) {
				return 1;
			}
			// sheet.getColumnWidth(), get the width (in units of 1/256th of a character width)
			float widthIn256 = sheet.getColumnWidth(col) * zoom;
			return (widthIn256 / 256 * fontWidth) + 1;
		}

		/**
		 * @see com.yss.sofa.report.engine#getRowPixelHeight
		 * @param sheet
		 * @param row
		 * @param zoom
		 * @return
		 */
		private float getRowPixelHeight(Sheet sheet, int row, float zoom) {

			if (sheet.getRow(row).getZeroHeight()) {
				return 1;
			}
			float h = sheet.getRow(row).getHeight() * zoom / 15f;
			return h + 1;// (h*96f/72f)+1;
		}
		
		/**
		 * 绘制PDF
		 * @param writer
		 * @param document
		 * @param cellInfo
		 * @throws DocumentException
		 * @throws IOException
		 */
		public void draw(PdfWriter writer, Document document, PdfTableCellInfo cellInfo) throws DocumentException, IOException {

			PdfPageInfo pageInfo = new PdfPageInfo(writer, document);
			List<PdfTableCell> splitColMapping = cellInfo.getCells();
			PdfFloatingCollection fc = new PdfFloatingCollection();
			fc.addFloating(cellInfo.floating);
			fc.maxWH = fc.getFloatingMaxWH(pageInfo.onePageFitWidth, pageInfo.onePageFitHeight);
			
			drawCells(writer, pageInfo, cellInfo, splitColMapping, fc);
		}

		/**
		 * 根据格子信息绘制格子. 先绘制行, 再绘制列.
		 * 
		 * @param writer
		 * @param pageInfo
		 * @param cellInfo
		 * @param cells
		 * @param fc
		 * @return
		 * @throws DocumentException
		 * @throws IOException
		 */
		public void drawCells(PdfWriter writer, PdfPageInfo pageInfo, PdfTableCellInfo cellInfo,
											List<PdfTableCell> cells, PdfFloatingCollection fc) throws DocumentException, IOException {

			if (cells == null || cells.size() == 0 
					|| pageInfo.pageHeight < 1 || pageInfo.pageFitHeight < 1 
					|| pageInfo.pageWidth < 1 || pageInfo.pageFitWidth < 1) {
				return;
			}

			boolean isSplitRow = false;
			boolean isSplitCol = false;

			List<PdfTableCell> splitColMapping = new ArrayList<PdfTableCell>();
			List<PdfTableCell> splitRowMapping = new ArrayList<PdfTableCell>();
			Map<Integer, List<PdfTableCell>> splitRowColMapping = new HashMap<Integer, List<PdfTableCell>>();
			splitRowColMapping.put(Integer.valueOf(1), splitRowMapping);

			// 要求cells中的cell是按rowIndex和colIndex顺序添加
			List<List<PdfTableCell>> rows = new ArrayList<List<PdfTableCell>>();
			List<PdfTableCell> row_ = new ArrayList<PdfTableCell>();
			rows.add(row_);
			for (int cellIndex = 0, cellSize = cells.size(), currentRowIndex = cells.get(0).rowIndex; cellIndex < cellSize; cellIndex++) {
				PdfTableCell xcell = cells.get(cellIndex);
				if (currentRowIndex == xcell.rowIndex) {
					row_.add(xcell); // 找出一行的全部格子, 按行为单位绘制
				} else {
					currentRowIndex = xcell.rowIndex;
					--cellIndex;
					row_ = new ArrayList<PdfTableCell>();
					rows.add(row_);
				}
			}
			
			Integer rightStartPageWidthNum = Integer.valueOf(2);
			
			for (List<PdfTableCell> row : rows) {
				
				PdfTableCell firstCell = row.get(0);
				float firstFitYPos = pageInfo.transferToFitYPos(firstCell.yPos);
				while (firstFitYPos >= pageInfo.pageMaxFitHeight) {
					// 绘制当页floating
					if (fc.hasFloating()) {
						drawFloating(pageInfo, cellInfo, fc, pageInfo.getNewWritingRows(), false);
					}
					
					if (splitColMapping.size() > 0) {
						drawSplitColCells(rightStartPageWidthNum, pageInfo, cellInfo, fc, splitColMapping, splitRowColMapping);
					} else {
						// 可能有表头或其他切分
						drawSplitColCellsForHeader(rightStartPageWidthNum, pageInfo, cellInfo, fc, splitRowColMapping);
					}
					
					pageInfo.newPage();
					
					// 表头必须在页面开始画
					processCellHeader(pageInfo, cellInfo, fc, splitRowColMapping);
					
					if (splitRowMapping.size() > 0) {
						writeSplitRowCells(pageInfo.pageHeightNum, pageInfo, cellInfo, splitRowMapping);
					}
				}
				
				Integer pageNum = pageInfo.getPageNum(firstCell.yPos);
				PdfContentByte[] canvases = pageInfo.getNewWritingRows();
				PdfPageInfo currentPageInfo = pageInfo.getPageInfo(pageNum);
				
				for (PdfTableCell cell : row) {
					float cellWidth = cell.drawWidth;
					float cellHeight = cell.drawHeight;
					float xPos = cell.xPos;
					float yPos = cell.yPos;
					cell.hasColSplit = false;
					cell.hasRowSplit = false;

					// 注意: PDF绘制(0, 0)位于页面左下角
					float drawXPos = pageInfo.transferToPageXPos(pageNum, xPos);

					if (drawXPos < 0) {
						if (drawXPos < -1) {
							continue;
						}
						// 在完全切分的范围内,这些格子最后新建一页再画
						splitColMapping.add(cell);
					} else {
						float drawYPos = pageInfo.transferToPageYPos(pageNum, yPos);
						float fitYPos = pageInfo.transferToFitYPos(yPos);
						isSplitRow = false;
						isSplitCol = false;

						if (fitYPos + cellHeight > currentPageInfo.pageFitHeight + 1) {
							// 存在行切分
							isSplitRow = true;
							splitRowMapping.add(cell);
							cell.nextSplitPageNum = pageNum.intValue() + 1;
						}

						if (drawXPos + cellWidth > pageInfo.pageFitWidth + 1) {
							// 存在列切分
							isSplitCol = true;
							splitColMapping.add(cell);
						}

						writeCell(cell, drawXPos, drawYPos, fitYPos, pageInfo, cellInfo, canvases, isSplitRow, isSplitCol);

						if (isSplitRow && isSplitCol) {
							cell.rowSplitDrawXPos = drawXPos;
							cell.rowSplitDrawYPos = pageInfo.onePageFitHeight + pageInfo.bottomMargin;
							
							float drawWidth = pageInfo.pageFitWidth - drawXPos;
							cell.drawCompleteWidth += drawWidth;
							float lastDrawWidth = cell.drawWidth - drawWidth;
							cell.xPos = pageInfo.onePageFitWidth * pageInfo.pageWidthNum;//xPos + drawWidth;
							cell.drawWidth = lastDrawWidth;
							
							cell.drawSplitWidth = drawWidth;

							cell.drawHeight = fitYPos + cellHeight - currentPageInfo.pageFitHeight;
							cell.drawSplitHeight = cell.drawHeight;
							cell.drawCompleteHeight = cell.getMaxHeight() - cell.drawHeight;
							cell.drawCompletePreHeight = currentPageInfo.pageFitHeight - fitYPos;
							
							cell.hasRowSplit = true;
							cell.hasColSplit = true;
							cell.hasRowColSplit = true;
						} else {					
	    					if (isSplitRow) {
	    						cell.rowSplitDrawXPos = drawXPos;
	    						cell.rowSplitDrawYPos = pageInfo.onePageFitHeight + pageInfo.bottomMargin;

	    						cell.drawHeight = fitYPos + cellHeight - currentPageInfo.pageFitHeight;
	    						cell.drawSplitHeight = cell.drawHeight;
	    						cell.drawCompleteHeight = cell.getMaxHeight() - cell.drawHeight;
	    						cell.drawCompletePreHeight = currentPageInfo.pageFitHeight - fitYPos;
	    						
	    						cell.hasRowSplit = true;
	    					} else if (isSplitCol) {
	    						float drawWidth = pageInfo.pageFitWidth - drawXPos;
	    						cell.drawCompleteWidth += drawWidth;
	    						float lastDrawWidth = cell.drawWidth - drawWidth;
	    						cell.xPos = pageInfo.onePageFitWidth * pageInfo.pageWidthNum;
	    						cell.colSplitDrawXPos = pageInfo.leftMargin;
	    						cell.drawWidth = lastDrawWidth;
	    						cell.hasColSplit = true;
	    					}
						}
					}
				}
				
				pageInfo.endWritingRows(canvases);
			}

			// 绘制当页floating
			if (fc.hasFloating()) {
				drawFloating(pageInfo, cellInfo, fc, pageInfo.getNewWritingRows(), false);
			}

			drawSplitColCells(rightStartPageWidthNum, pageInfo, cellInfo, fc, splitColMapping, splitRowColMapping);
			
			// 绘制最后的行切
			drawLastSplitRowCells(pageInfo, cellInfo, fc, splitRowColMapping);
			
			// 如果还有浮动对象,则将其剩下部分绘制完
			if (fc.hasFloating()) {
				drawBottomFloating(pageInfo, cellInfo, fc);
			}
		}
		
		private void drawLastSplitRowCells(PdfPageInfo originalPageInfo, PdfTableCellInfo cellInfo, PdfFloatingCollection fc, 
		                                   Map<Integer, List<PdfTableCell>> splitRowColMapping) throws DocumentException, IOException {
			Integer startPageWidthNum = Integer.valueOf(1);
			boolean flag = hasDrawSplitColCellsForSplitRow(startPageWidthNum, originalPageInfo, cellInfo, fc, splitRowColMapping);
			while (flag) {
				//originalPageInfo.addNewPage();
				//flag = drawSplitColCellsForSplitRow(startPageWidthNum, originalPageInfo, cellInfo, fc, splitRowColMapping);
				
				// 表头必须在页面开始画
				if (fc.hasFloating()) {
					drawFloating(originalPageInfo, cellInfo, fc, originalPageInfo.getNewWritingRows(), false);
				}
				originalPageInfo.newPage();
				processCellHeader(originalPageInfo, cellInfo, fc, splitRowColMapping);
				drawSplitColCellsForHeader(startPageWidthNum, originalPageInfo, cellInfo, fc, splitRowColMapping);
				flag = drawSplitColCellsForSplitRow(startPageWidthNum, originalPageInfo, cellInfo, fc, splitRowColMapping);
			}
		}
		
		private void processCellHeader(PdfPageInfo pageInfo, PdfTableCellInfo cellInfo, PdfFloatingCollection fc, Map<Integer, List<PdfTableCell>> splitRowColMapping) {
			// 表头必须在页面开始画
			if (cellInfo.headerInfo != null) {
				List<PdfTableCell> headerCells = cellInfo.headerInfo.pageNumHeaderMapping.remove(pageInfo.pageHeightNum);
				if (headerCells == null || headerCells.size() == 0) {
					return;
				}
				
				// 改变行切与表头连接格子的YPos
				float offset = cellInfo.headerInfo.headerHeight;
				int pageWidthNumInt = 1;
				int pageHeightNumInt = pageInfo.pageHeightNum;
				do {
					Integer nextPageWidthNum = Integer.valueOf(pageWidthNumInt);
					List<PdfTableCell> splitRow = splitRowColMapping.get(nextPageWidthNum);
					if (splitRow != null) {
						if (splitRow.size() > 0) {
							for (PdfTableCell xcell : splitRow) {
								// 注意: PDF绘制(0, 0)位于页面左下角
								if (xcell.drawCompleteHeight > 0) {
									xcell.rowSplitDrawYPos = pageInfo.transferToPageYPos(pageHeightNumInt, xcell.rowSplitDrawYPos - pageInfo.bottomMargin + offset); //xcell.rowSplitDrawYPos += offset;
									xcell.drawSplitHeightOffset = offset;
									xcell.nextSplitPageNum = pageHeightNumInt;
									
									// (xcell.originalXPos + xcell.drawCompleteWidthXPos) % pageInfo.onePageFitWidth == 0)
									int m = (int)remainderToFloat((xcell.originalXPos + xcell.drawCompleteWidthXPos + 0.1f), pageInfo.onePageFitWidth);
									boolean isStartXPos = (xcell.xPos == 0 || m < 1) ? true : false;
									if (isStartXPos && pageWidthNumInt > 1) {
										xcell.drawCompleteWidth = xcell.drawCompleteWidthXPos;
									} else {
										xcell.drawCompleteWidth = 0;
									}
								}
							}
							List<PdfTableCell> cells = getSplitRowCellHeader(pageInfo, pageHeightNumInt, nextPageWidthNum, headerCells);
							if (cells.size() > 0) {
								splitRow.addAll(0, cells);
							}
						}
					} else {
						break;
					}
					++pageWidthNumInt;
				} while (true);
			}
		}
		
		// 需要保证表头高度不能跨页
		private List<PdfTableCell> getSplitRowCellHeader(PdfPageInfo pageInfo, int nextSplitPageNum, int pageNum, List<PdfTableCell> headerCells) {
			List<PdfTableCell> result = new ArrayList<PdfTableCell>();
			float pageStartXPos = pageInfo.onePageFitWidth * (pageNum - 1);
			float pageEndXPos = pageInfo.onePageFitWidth * pageNum;
			for (PdfTableCell xcell : headerCells) {
				float xPos = xcell.xPos;
				float cellWidth = xcell.maxWidth;
				if (xPos >= pageStartXPos && xPos < pageEndXPos) {
					xcell = PdfTableCell.copyCell(xcell);
					result.add(xcell);
					
					if (xPos + cellWidth > pageEndXPos) {
						xcell.drawSplitWidth = pageEndXPos - xPos;
						xcell.drawWidth = xcell.drawSplitWidth;
						xcell.hasColSplit = true;
					} else {
						xcell.drawSplitWidth = cellWidth;
					}
					
					xcell.drawSplitHeight = xcell.getMaxHeight();
					xcell.rowSplitDrawXPos = pageInfo.leftMargin + remainderToFloat(xPos, pageInfo.onePageFitWidth); //(xPos % pageInfo.onePageFitWidth);
					xcell.rowSplitDrawYPos = pageInfo.transferToPageYPos(nextSplitPageNum, xcell.yPos);
					xcell.isHeader = true;
					xcell.nextSplitPageNum = nextSplitPageNum;
					
				} else if (xPos < pageStartXPos && xPos + cellWidth > pageStartXPos) {
					xcell = PdfTableCell.copyCell(xcell);
					result.add(xcell);

					xcell.drawSplitWidth = (xPos + cellWidth) - pageStartXPos;
					if (xcell.drawSplitWidth > pageInfo.onePageFitWidth) {
						xcell.drawSplitWidth = pageInfo.onePageFitWidth;
					}
					xcell.drawWidth = xcell.drawSplitWidth;

					xcell.drawSplitHeight = xcell.getMaxHeight();
					xcell.rowSplitDrawXPos = pageInfo.leftMargin;
					xcell.rowSplitDrawYPos = pageInfo.transferToPageYPos(nextSplitPageNum, xcell.yPos);
					xcell.hasColSplit = true;
					xcell.isHeader = true;
					xcell.nextSplitPageNum = nextSplitPageNum;
					
					xcell.drawCompleteWidth = pageStartXPos - xPos;
					xcell.drawCompleteWidthXPos = xcell.drawCompleteWidth; //xcell.maxWidth - xcell.drawSplitWidth;
				}
			}
			return result;
		}
		
		private void drawSplitColCellsForHeader(Integer pageWidthNum, PdfPageInfo originalPageInfo, PdfTableCellInfo cellInfo, PdfFloatingCollection fc, 
		                                        Map<Integer, List<PdfTableCell>> splitRowColMapping) throws DocumentException, IOException {
			// 新建一页
			Integer pageNum = Integer.valueOf(originalPageInfo.pageHeightNum);
			
			// 先将上一行剩余部分画完
			for (int i = pageWidthNum.intValue(), j = splitRowColMapping.size(); i <= j; ++i) {
				PdfPageInfo pageInfo = originalPageInfo.newPageForHorizontal(pageNum);
				List<PdfTableCell> splitRow = splitRowColMapping.get(Integer.valueOf(i));
				if (splitRow.size() > 0) {
					writeSplitRowCells(pageNum, pageInfo, cellInfo, splitRow);
				}
			}
		}
		
		private void drawSplitColCells(Integer pageWidthNum, PdfPageInfo originalPageInfo, PdfTableCellInfo cellInfo, PdfFloatingCollection fc,
									   List<PdfTableCell> cells, Map<Integer, List<PdfTableCell>> splitRowColMapping) throws DocumentException, IOException {

			// 绘制右侧内容
			if (cells.size() > 0) {
				
				// 新建一页
				Integer pageNum = Integer.valueOf(originalPageInfo.pageHeightNum);
				PdfPageInfo pageInfo = originalPageInfo.newPageForHorizontal(pageNum);
				
				// 先将上一行剩余部分画完
				List<PdfTableCell> splitRow = splitRowColMapping.get(pageWidthNum);
				if (splitRow == null) {
					splitRow = new ArrayList<PdfTableCell>();
					splitRowColMapping.put(pageWidthNum, splitRow);
				}
				if (splitRow.size() > 0) {
					writeSplitRowCells(pageNum, pageInfo, cellInfo, splitRow);
				}
				
				List<List<PdfTableCell>> rows = new ArrayList<List<PdfTableCell>>();
				List<PdfTableCell> row_ = new ArrayList<PdfTableCell>();
				rows.add(row_);
				for (int cellIndex = 0, cellSize = cells.size(), currentRowIndex = cells.get(0).rowIndex; cellIndex < cellSize; cellIndex++) {
					PdfTableCell xcell = cells.get(cellIndex);
					if (currentRowIndex == xcell.rowIndex) {
						if (xcell.hasColSplit) {
							xcell = PdfTableCell.copyCell(xcell);
							xcell.drawCompleteWidthXPos = xcell.drawCompleteWidth;
						}
						row_.add(xcell); // 找出一行的全部格子, 按行为单位绘制
					} else {
						currentRowIndex = xcell.rowIndex;
						--cellIndex;
						row_ = new ArrayList<PdfTableCell>();
						rows.add(row_);
					}
				}
				
				boolean isSplitRow = false;
				boolean isSplitCol = false;
				
				List<PdfTableCell> splitColMapping = new ArrayList<PdfTableCell>();
				PdfTableCell xcell = null;
				boolean isRowColSplitCell = false;
				
				for (List<PdfTableCell> row : rows) {
					
					PdfContentByte[] canvases = pageInfo.getNewWritingRows();
					PdfPageInfo currentPageInfo = pageInfo;
					
					for (PdfTableCell cell : row) {
						float cellWidth = cell.drawWidth;
						float cellHeight = cell.drawHeight + cell.drawCompletePreHeight; //cell.getMaxHeight();
						float xPos = cell.xPos;
						float yPos = cell.yPos;

						// 注意: PDF绘制(0, 0)位于页面左下角
						float drawXPos = pageInfo.transferToPageXPos(pageNum, xPos);

						if (drawXPos < 0) {
							if (drawXPos < -1) {
								continue;
							}
							// 在完全切分的范围内,这些格子最后新建一页再画
							splitColMapping.add(cell);
						} else {
							float drawYPos = pageInfo.transferToPageYPos(pageNum, yPos);
							float fitYPos = pageInfo.transferToFitYPos(yPos);
							isSplitRow = false;
							isSplitCol = false;
							isRowColSplitCell = false;

							if (fitYPos + cellHeight > currentPageInfo.pageFitHeight + 1) {
								// 存在行切分
								isSplitRow = true;
								//splitRowMapping.add(cell);
								
								if (cell.hasRowColSplit) {
									isRowColSplitCell = true;
								} else {
									splitRow.add(cell);
								}
								
								cell.nextSplitPageNum = pageNum.intValue() + 1;
							}

							if (drawXPos + cellWidth > pageInfo.pageFitWidth + 1) {
								// 存在列切分
								isSplitCol = true;
								splitColMapping.add(cell);
							}

							//cell.drawCompleteWidthXPos = cell.drawCompleteWidth;
							
							float drawCompleteHeight = cell.drawCompleteHeight;
							cell.drawCompleteHeight = drawCompleteHeight - cell.drawCompletePreHeight;
							writeCell(cell, drawXPos, drawYPos, fitYPos, pageInfo, cellInfo, canvases, isSplitRow, isSplitCol);
							cell.drawCompleteHeight = drawCompleteHeight;
							
							if (isSplitRow && isSplitCol) {
								cell.rowSplitDrawXPos = drawXPos;
								
								float drawWidth = pageInfo.pageFitWidth - drawXPos;
								cell.drawCompleteWidth += drawWidth;
								float lastDrawWidth = cell.drawWidth - drawWidth;
								cell.xPos = pageInfo.onePageFitWidth * pageInfo.pageWidthNum; //xPos + drawWidth;
								cell.drawWidth = lastDrawWidth;
								
								cell.drawSplitWidth = drawWidth;
								
								if (!cell.hasColSplit) {
									cell.rowSplitDrawYPos = pageInfo.onePageFitHeight + pageInfo.bottomMargin;
    								cell.drawHeight = fitYPos + cellHeight - currentPageInfo.pageFitHeight;
    								cell.drawSplitHeight = cell.drawHeight;
    								cell.drawCompleteHeight = cell.getMaxHeight() - cell.drawHeight;
    								cell.drawCompletePreHeight = currentPageInfo.pageFitHeight - fitYPos;
								}
								
								cell.hasRowSplit = true;
								cell.hasColSplit = true;
								cell.hasRowColSplit = true;
								
	    						if (isRowColSplitCell) {
	    							xcell = PdfTableCell.copyCell(cell);
	    							splitRow.add(xcell);
	    						}
							} else {					
		    					if (isSplitRow) {
		    						if (!cell.hasColSplit) {
			    						cell.rowSplitDrawXPos = drawXPos;
			    						cell.rowSplitDrawYPos = pageInfo.onePageFitHeight + pageInfo.bottomMargin;
			    						
    		    						cell.drawHeight = fitYPos + cellHeight - currentPageInfo.pageFitHeight;
    		    						cell.drawSplitHeight = cell.drawHeight;
    		    						cell.drawCompleteHeight = cell.getMaxHeight() - cell.drawHeight;
    		    						cell.drawCompletePreHeight = currentPageInfo.pageFitHeight - fitYPos;
		    						}
		    						
		    						cell.hasRowSplit = true;
		    						
		    						if (isRowColSplitCell) {
		    							xcell = PdfTableCell.copyCell(cell);
		    							xcell.rowSplitDrawXPos = drawXPos;
		    							xcell.drawSplitWidth = cell.maxWidth - cell.drawCompleteWidth;
		    							xcell.hasColSplit = false;
		    							splitRow.add(xcell);
		    						}
		    					} else if (isSplitCol) {
		    						float drawWidth = pageInfo.pageFitWidth - drawXPos;
		    						cell.drawCompleteWidth += drawWidth;
		    						float lastDrawWidth = cell.drawWidth - drawWidth;
		    						cell.xPos = pageInfo.onePageFitWidth * pageInfo.pageWidthNum;
		    						cell.colSplitDrawXPos = pageInfo.leftMargin;
		    						cell.drawWidth = lastDrawWidth;
		    						cell.hasColSplit = true;
		    					}
							}
						}
					}
					
					pageInfo.endWritingRows(canvases);
				}
				
				// 绘制当页floating
				if (fc.hasFloating()) {
					drawFloating(pageInfo, cellInfo, fc, pageInfo.getNewWritingRows(), false);
				}
				
				if (splitColMapping.size() > 0) {
					// 递归绘制
					drawSplitColCells(Integer.valueOf(pageWidthNum.intValue() + 1), pageInfo, cellInfo, fc, splitColMapping, splitRowColMapping);
				}
				
				cells.clear();
				
			} else {
				if (hasDrawSplitColCellsForSplitRow(pageWidthNum, originalPageInfo, cellInfo, fc, splitRowColMapping)) {
					drawSplitColCellsForSplitRow(pageWidthNum, originalPageInfo, cellInfo, fc, splitRowColMapping);
				}
			}
			
		}
		
		private boolean drawSplitColCellsForSplitRow(Integer pageWidthNum, PdfPageInfo originalPageInfo, PdfTableCellInfo cellInfo, PdfFloatingCollection fc, 
		                  Map<Integer, List<PdfTableCell>> splitRowColMapping) throws DocumentException, IOException {
			boolean flag = false;
			
			List<PdfTableCell> splitRow = splitRowColMapping.get(pageWidthNum);
			if (splitRow == null) {
				return flag;
			}
			
			Integer pageNum = Integer.valueOf(originalPageInfo.pageHeightNum);
			PdfPageInfo pageInfo = originalPageInfo.newPageForHorizontal(pageNum);
			if (splitRow.size() > 0) {
				// 绘制当页floating
				if (fc.hasFloating()) {
					drawFloating(pageInfo, cellInfo, fc, pageInfo.getNewWritingRows(), false);
				}
				
				writeSplitRowCells(pageNum, pageInfo, cellInfo, splitRow);
				
				flag = splitRow.size() > 0;
			}
			
			int pageWidthNumInt = pageWidthNum.intValue();
			do {
				Integer nextPageWidthNum = Integer.valueOf(++pageWidthNumInt);
				splitRow = splitRowColMapping.get(nextPageWidthNum);
				if (splitRow != null) {
					pageInfo = pageInfo.newPageForHorizontal(pageNum);
					if (splitRow.size() > 0) {
						// 绘制当页floating
						if (fc.hasFloating()) {
							drawFloating(pageInfo, cellInfo, fc, pageInfo.getNewWritingRows(), false);
						}
						
						writeSplitRowCells(pageNum, pageInfo, cellInfo, splitRow);
						
						if (!flag) {
							flag = true;
						}
					}
				} else {
					break;
				}
			} while (true);
			
			return flag;
		}
		
		private boolean hasDrawSplitColCellsForSplitRow(Integer pageWidthNum, PdfPageInfo originalPageInfo, PdfTableCellInfo cellInfo, PdfFloatingCollection fc, 
		                      		                  Map<Integer, List<PdfTableCell>> splitRowColMapping) throws DocumentException, IOException {
			int pageWidthNumInt = pageWidthNum.intValue();
			
			do {
				Integer nextPageWidthNum = Integer.valueOf(pageWidthNumInt++);
				List<PdfTableCell> splitRow = splitRowColMapping.get(nextPageWidthNum);
				if (splitRow != null) {
					if (splitRow.size() > 0) {
						return true;
					}
				} else {
					break;
				}
			} while (true);
			
			return false;
		}
		
		/**
		 * 绘制下方浮动对象(先画行再画列时将超过表格下边界的浮动对象绘制完毕)
		 * @param pageInfo
		 * @param cellInfo
		 * @param fc
		 */
		private void drawBottomFloating(PdfPageInfo pageInfo, PdfTableCellInfo cellInfo, PdfFloatingCollection fc) {
			if (!fc.hasFloating()) { // 如果完整页面后应该有空白页, 这里将其去掉
				return;
			}
			
			if (fc.maxWH == null) {
				fc.maxWH = fc.getFloatingMaxWH(pageInfo.onePageFitWidth, pageInfo.onePageFitHeight);
			}
			
			int w = fc.maxWH[0];
			int h = fc.maxWH[1];
			if (w == 0 && h == 0) {
				return;
			}
			
			// 先画行保证当前页已经画完, 所以只有当h > pageInfo.pageHeightSplitNum时再画
			if (h > pageInfo.pageHeightSplitNum) {
				float pageWidth = pageInfo.onePageFitWidth;
				float pageHeight = pageInfo.onePageFitHeight;
				for (int heightNum = pageInfo.pageHeightSplitNum; heightNum <= h; ++heightNum) {
					PdfPageInfo pageInfoForRow = pageInfo.newPage();
					
					List<PdfFloating> floatingList = fc.getFloating(pageInfoForRow.pageHeightSplitNum, 1, pageWidth, pageHeight);
					if (floatingList != null && floatingList.size() > 0) {
						PdfContentByte[] canvases = pageInfoForRow.getNewWritingRows();
						writeFloating(pageInfoForRow, cellInfo, fc, canvases, floatingList);
					}
					if (w > 1) {
						int pageHeightSplitNum = pageInfoForRow.pageHeightSplitNum;
						for (int widthNum = 2; widthNum <= w; ++widthNum) {
							pageInfoForRow = pageInfoForRow.newPageForHorizontal(Integer.valueOf(widthNum));
							floatingList = fc.getFloating(pageHeightSplitNum, widthNum, pageWidth, pageHeight);
							if (floatingList != null && floatingList.size() > 0) {
								writeFloating(pageInfoForRow, cellInfo, fc, pageInfoForRow.getNewWritingRows(), floatingList);
							}
							
							if (!fc.hasFloating()) {
								return; // 后面空白页废弃
							}
						}
					}
					
					if (!fc.hasFloating()) {
						return; // 后面空白页废弃
					}
				}
			}
		}

		/**
		 * 绘制浮动对象
		 * @param pageInfo
		 * @param cellInfo
		 * @throws Exception 
		 */
		private void drawFloating(PdfPageInfo pageInfo, PdfTableCellInfo cellInfo, PdfFloatingCollection fc, PdfContentByte[] canvases, boolean drawAll) {
			if (!fc.hasFloating()) {
				return;
			}
			
			if (drawAll) {
				if (fc.maxWH == null) {
					fc.maxWH = fc.getFloatingMaxWH(pageInfo.onePageFitWidth, pageInfo.onePageFitHeight);
				}
				
				int w = fc.maxWH[0];
				int h = fc.maxWH[1];
				if (w == 0 && h == 0) {
					return;
				}
				
				if (w > pageInfo.pageWidthNum) {
					
					pageInfo.pageWidthNum++;
					
					float pageWidth = pageInfo.onePageFitWidth;
					float pageHeight = pageInfo.onePageFitHeight;
					for (int widthNum = pageInfo.pageWidthNum; widthNum <= w; widthNum++) {
						
						int pageCount = h;
						
						for (int heightNum = 1, newPageNum = 0; heightNum <= h; heightNum++) {
							++newPageNum;
							
							List<PdfFloating> floatingList = fc.getFloating(heightNum, widthNum, pageWidth, pageHeight);


							if (floatingList != null && floatingList.size() > 0) {
								for (int i = 0; i < newPageNum; i++) {
									pageInfo.newPage();
									--pageCount;
								}
								canvases = pageInfo.getNewWritingRows();
								writeFloating(pageInfo, cellInfo, fc, canvases, floatingList);
								pageInfo.endWritingRows(canvases);
								newPageNum = 0;
								
								if (!fc.hasFloating()) {
									break; // 补全最后的空页不退出
									//return;
								}
							}
						}

						// 整列空的补全, 列后空页补全
						if (pageCount > 0) {
							for (int i = 0; i < pageCount; i++) {
								pageInfo.newPage();
							}
						}
						
						pageInfo.pageWidthNum++;
						
					} // end for widthNum

				}
				

			} else {
				List<PdfFloating> floatingList = fc.getFloating(pageInfo.pageHeightSplitNum, pageInfo.pageWidthNum, pageInfo.onePageFitWidth, pageInfo.onePageFitHeight);
				writeFloating(pageInfo, cellInfo, fc, canvases, floatingList);
				pageInfo.endWritingRows(canvases);
			}

		}
		
		private void writeFloating(PdfPageInfo pageInfo, PdfTableCellInfo cellInfo, PdfFloatingCollection fc, PdfContentByte[] canvases, List<PdfFloating> floatingList) {
			if (floatingList == null || floatingList.size() == 0) {
				return;
			}
			
			PdfContentByte textCanvas = canvases[PdfPTable.TEXTCANVAS];
			
			try {
	    		for (PdfFloating floating : floatingList) {
	    			// 目前只处理图片
	    			Image image = floating.cell.getImage();
	    			if (image != null) {
	    				image = clipImage(floating, image, cellInfo.imageZoom);
	    				
	    				float x = remainderToFloat(floating.drawXPos, pageInfo.onePageFitWidth) + pageInfo.leftMargin;
	    				float y = pageInfo.onePageFitHeight - (remainderToFloat(floating.drawYPos, pageInfo.onePageFitHeight)) - image.getScaledHeight() + pageInfo.bottomMargin;
	    				
	    				// use addImage(image, image_width, 0, 0, image_height, x, y).
	    				textCanvas.addImage(image, 
	    				                    image.getScaledWidth(), 0, 0, 
	    				                    image.getScaledHeight(), x, y);
	    			}
	    		}
			} catch (Exception e) {
				e.printStackTrace();
			}
			
			pageInfo.endWritingRows(canvases);
		}

		/**
		 * 绘制单元格
		 * @param cell
		 * @param drawXPos
		 * @param drawYPos
		 * @param pageInfo
		 * @param cellInfo
		 * @param canvases
		 * @param isSplitRow
		 * @param isSplitCol
		 * @throws DocumentException 
		 * @throws IOException 
		 */
		private void writeCell(PdfTableCell cell, float drawXPos, float drawYPos, float fitYPos, PdfPageInfo pageInfo, PdfTableCellInfo cellInfo, PdfContentByte[] canvases,
							   boolean isSplitRow, boolean isSplitCol) throws DocumentException, IOException {
			// 注意: PDF绘制(0, 0)位于页面左下角
	    	//System.out.println("writeCell: (" + drawXPos + " , " + drawYPos + ") rowSplit[" + isSplitRow + "] colSplit[" + isSplitCol + "]");
	    	
    		int drawXPosInt = (int) (drawXPos + 0.1f);
    		int leftMarginInt = (int) (pageInfo.leftMargin + 0.1f);
			
	    	DrawRectangle rect = null;
	    	
	    	boolean isDrawWholeCell = false;
	    	if (!isSplitRow && !isSplitCol) {
	    		// 完整的画一个格子
	    		float w = cell.drawWidth;
	    		float h = cell.drawHeight;
	    		float llx = drawXPos;
	    		float lly = drawYPos - h;
	    		float urx = drawXPos + w;
	    		float ury = drawYPos;
	    		rect = new DrawRectangle(llx, lly, urx, ury, drawXPos, drawYPos, w, h);
	    		
	    		if (cell.drawCompleteWidth > 0 && drawXPosInt == leftMarginInt && w < cell.maxWidth) {
	    			// 如果是列切分的第一个格子, 则不需要画左边框
	    			//rect.setBorder(Rectangle.TOP + Rectangle.RIGHT + Rectangle.BOTTOM);
	    			
	    			// $Date: 2016-11-30 - 新需求要画上左边框
	    			rect.setBorder(Rectangle.BOX);
	    			rect.drawBorderForceLeft = true;
	    		} else {
	    			rect.setBorder(Rectangle.BOX);
	    			
	    			isDrawWholeCell = true;
	    		}

	    	} else if (isSplitRow && !isSplitCol) {
	    		// 只切分行
	    		float w = cell.drawWidth;
	    		float h = pageInfo.pageFitHeight - fitYPos;
	    		float llx = drawXPos;
	    		float lly = pageInfo.bottomMargin;
	    		float urx = drawXPos + w;
	    		float ury = drawYPos;
	    		rect = new DrawRectangle(llx, lly, urx, ury, drawXPos, drawYPos, w, h);
	    		
	    		if (cell.drawCompleteWidth > 0 && drawXPosInt == leftMarginInt && w < cell.maxWidth) {
	    			//rect.setBorder(Rectangle.TOP + Rectangle.RIGHT);
	    			
	    			// $Date: 2016-12-8 - 新需求要画上左边框
	    			rect.setBorder(DrawRectangle.TOP + DrawRectangle.LEFT + DrawRectangle.RIGHT);
	    			rect.drawBorderForceLeft = true;
	    		} else {
	    			rect.setBorder(Rectangle.TOP + Rectangle.LEFT + Rectangle.RIGHT);
	    		}

	    	} else if (!isSplitRow && isSplitCol) {
	    		// 只切分列
	    		float w = pageInfo.pageFitWidth - drawXPos;
	    		float h = cell.drawHeight;
	    		float llx = drawXPos;
	    		float lly = drawYPos - h;
	    		float urx = drawXPos + w;
	    		float ury = drawYPos;
	    		rect = new DrawRectangle(llx, lly, urx, ury, drawXPos, drawYPos, w, h);
	    		
	    		if (cell.drawCompleteWidth > 0 && drawXPosInt == leftMarginInt && w < cell.maxWidth) {
	    			
	    			//if ((int)(cell.drawCompleteWidth + w + 0.1f) == (int)(cell.maxWidth + 0.1f)) {
	    			//	rect.setBorder(Rectangle.TOP + Rectangle.RIGHT + Rectangle.BOTTOM);
	    			//} else {
	    			//	rect.setBorder(Rectangle.TOP + Rectangle.BOTTOM);
	    			//}
	    			
	    			// $Date: 2016-11-30 - 新需求要画上左右边框
	    			rect.setBorder(Rectangle.BOX);
	    			rect.drawBorderForceLeft = true;
	    			rect.drawBorderForceRight = true;
	    		} else {
	    			//rect.setBorder(Rectangle.TOP + Rectangle.LEFT + Rectangle.BOTTOM);
	    			
	    			// $Date: 2016-11-30 - 新需求要画上右边框
	    			rect.setBorder(Rectangle.BOX);
	    			rect.drawBorderForceRight = true;
	    		}
	    		
	    	} else {
	    		// 行列都切分(通常是页面右下角超出页边部分)
	    		float w = pageInfo.pageFitWidth - drawXPos;
	    		float h = pageInfo.pageFitHeight - fitYPos;
	    		float llx = drawXPos;
	    		float lly = drawYPos - h;
	    		float urx = drawXPos + w;
	    		float ury = drawYPos;
	    		rect = new DrawRectangle(llx, lly, urx, ury, drawXPos, drawYPos, w, h);
	    		//if (cell.drawCompleteWidth > 0 && drawXPosInt == leftMarginInt && w < cell.maxWidth) {
	    		//	// 如果是列切分的第一个格子, 则不需要画左边框
	    		//	rect.setBorder(Rectangle.TOP);
	    		//} else {
	    		//	rect.setBorder(Rectangle.TOP + Rectangle.LEFT);
	    		//}
	    		
	    		// $Date: 2016-11-30 - 新需求要画上左右边框
	    		rect.setBorder(Rectangle.TOP + Rectangle.LEFT + Rectangle.RIGHT);
	    		rect.drawBorderForceLeft = true;
	    		rect.drawBorderForceRight = true;
	    	}
	    	
    		writeBorderAndBackground(cell, rect, canvases);
    		
    		writeText(pageInfo.writer, cell, rect, canvases, isDrawWholeCell);
    		
    		if (cell.getImage() != null) {
    			writeImage(cell, rect, canvases, cellInfo.imageZoom);
    		}

		}
		
		/**
		 * 绘制单元格文本内容
		 * @param cell
		 * @param rect
		 * @param canvases
		 * @throws DocumentException 
		 * @throws IOException 
		 */
		public void writeText(PdfWriter writer, PdfTableCell xcell, DrawRectangle rect, PdfContentByte[] canvases, boolean isDrawWholeCell) throws DocumentException, IOException {
			if (xcell.textControl == null) {
				return;
			}
			
			switch (xcell.textControl) {
				case CLIP : {
					if (isDrawWholeCell && xcell.textDrawControl == TextControlEnum.DIRECT) {
						writeTextForWholeCell(xcell, rect, canvases); // 优化提示: 能直接画完内容的格子不需要进行裁剪, 每次裁剪会对内容做3次复制会增加计算和存储空间
					} else {
						writeTextForClipCell(writer, xcell, rect, canvases);
					}
					break;
				}
				case SHRINK : {
					if (isDrawWholeCell) {
						writeTextForWholeCell(xcell, rect, canvases);
					} else {
						writeTextForClipCell(writer, xcell, rect, canvases);
					}
					break;
				}
				case STRETCH : {
					if (isDrawWholeCell && xcell.textDrawControl == TextControlEnum.DIRECT) {
						writeTextForWholeCell(xcell, rect, canvases);
					} else {
						writeTextForClipCell(writer, xcell, rect, canvases);
					}
					break;
				}
				case AUTOSIZE : {
					if (isDrawWholeCell && xcell.textDrawControl == TextControlEnum.DIRECT) {
						writeTextForWholeCell(xcell, rect, canvases);
					} else {
						writeTextForClipCell(writer, xcell, rect, canvases);
					}
					break;
				}
				default : {
					writeTextForClipCell(writer, xcell, rect, canvases);
					break;
				}
			}
			
		}
		
		/**
		 * 绘制不完整的格子.
		 * 
		 * @param writer
		 * @param cell
		 * @param rect
		 * @param canvases
		 * @throws DocumentException
		 * @throws IOException
		 */
		public void writeTextForClipCell(PdfWriter writer, PdfTableCell cell, DrawRectangle rect, PdfContentByte[] canvases) throws DocumentException, IOException {
			PdfContentByte canvas = canvases[PdfPTable.TEXTCANVAS];
			
			PdfTemplate clip = getClipContentTemplate(cell, rect, canvas);
			
			if (clip != null) {
				float x = rect.getDrawXPos() + (cell.drawCompleteWidthXPos > 0 ? 0 : rect.getDrawBorderWidthLeft());
				float y = rect.getDrawYPos() - (cell.drawCompleteHeight > 0 ? 0 : rect.getDrawBorderWidthTop());
		        
		        writer.releaseTemplate(clip);
				canvas.addTemplate(clip, x,  y - clip.getHeight());
			}
		}
		
		/**
		 * 绘制完整的格子.
		 * 
		 * @param cell
		 * @param rect
		 * @param canvases
		 * @throws DocumentException
		 * @see com.itextpdf.text.pdf.ColumnText#go
		 */
		public void writeTextForWholeCell(PdfTableCell cell, DrawRectangle rect, PdfContentByte[] canvases) throws DocumentException {
			
			Phrase phrase = cell.getTextPhrase();
			if (phrase == null || phrase.getContent().length() == 0) {
				return;
			}
			
			PdfTextInfo textInfo = getPdfTextInfo(cell, phrase);
			if (textInfo.width < 1) {
				return;
			}
			
			float xPos = rect.getDrawXPos();
			float yPos = rect.getDrawYPos();
			float cellDrawWidth = cell.maxWidth;
			float cellDrawHeight = cell.getMaxHeight();
			
			Font f1 = cell.fontSelector.getFont(0);
			float fontSize = textInfo.fontSize;
			float asc = f1.getBaseFont().getFontDescriptor(BaseFont.ASCENT, fontSize);
			float desc = f1.getBaseFont().getFontDescriptor(BaseFont.DESCENT, fontSize);
			float leading = asc - desc;
			
			float llx = xPos + cell.leftBorderWidth; //rect.getDrawBorderWidthLeft()
			float lly = yPos - cellDrawHeight + cell.bottomBorderWidth;
			float urx = xPos + cellDrawWidth - cell.rightBorderWidth;
			float ury = yPos - cell.topBorderWidth;
			float tly = ury;
			
			float cellContentHeight = cellDrawHeight - cell.topBorderWidth - cell.bottomBorderWidth;
			float textHeight = textInfo.height - desc/* + 1*/; // 保证能画下, 确保垂直对齐时不太偏下(pdf绘制文本时, 如果高度不够不会绘制)
			if (cellContentHeight > textHeight) {
	    		switch (cell.getVerticalAlignment()) {
	    			case Rectangle.ALIGN_MIDDLE : {
	    				tly -= (cellContentHeight - textHeight) / 2f;
	    				break;
	    			}
	    			case Rectangle.ALIGN_BOTTOM : {
	    				tly -= (cellContentHeight - textHeight);
	    				break;
	    			}
	    			default : {
	    				break;
	    			}
	    		}
			} else {
				// 此种情况Excel对垂直无效果,按top显示
				if (cell.getVerticalAlignment() == Rectangle.ALIGN_BOTTOM) {
					tly += -desc;
				}
			}
			
	        ColumnText ct = new ColumnText(null);
	        ct.setCanvases(canvases);
	        
	        if (f1.isBold()) {
	        	--urx; // 加粗字体避免压线
	        }
	        
    		ct.setSimpleColumn(phrase, 
    		                   llx, lly, urx, /*ury*/tly,
    		                   leading,
    		                   cell.getHorizontalAlignment());
    		
    		ct.go();
		}
		
		private PdfTextInfo getPdfTextInfo(PdfTableCell cell, Phrase phrase) {
			PdfTextInfo textInfo = cell.textInfo;
			if (textInfo == null) {
				if (phrase == null || cell.fontSelector == null) {
					textInfo = new PdfTextInfo();
					cell.textInfo = textInfo;
					return textInfo;
				}
				
				Font firstFont = cell.fontSelector.getFont(0);
				textInfo = new PdfTextInfo();
				//textInfo.leading = firstFont.getCalculatedLeading(1.5f);
				textInfo.fontSize = firstFont.getSize();
				List<Chunk> chunkList = phrase.getChunks();
				processTextInfo(chunkList, textInfo);
				cell.textInfo = textInfo;
			}
			return textInfo;
		}
		
		/* 是否显示切分文本的绘制边框.调试使用 */
		public static boolean displayTextDrawBorder = false;
		
		/**
		 * 获得适合的文本内容
		 * @param cell
		 * @param rect
		 * @param canvas
		 * @return
		 * @throws DocumentException
		 */
		public PdfTemplate getClipContentTemplate(PdfTableCell cell, DrawRectangle rect, PdfContentByte canvas) throws DocumentException {
			Phrase phrase = cell.getTextPhrase();
			if (phrase == null || phrase.getContent().length() == 0) {
				return null;
			}
			
			PdfTextInfo textInfo = getPdfTextInfo(cell, phrase);
			
			if (textInfo.width < 1) {
				return null;
			}

			float cellDrawWidth = cell.maxWidth;
			float cellDrawHeight = cell.getMaxHeight();
			
			Font f1 = cell.fontSelector.getFont(0);
			float fontSize = textInfo.fontSize;
			float asc = f1.getBaseFont().getFontDescriptor(BaseFont.ASCENT, fontSize);
			float desc = f1.getBaseFont().getFontDescriptor(BaseFont.DESCENT, fontSize);
			float leading = asc - desc;

			float tly = 0;;
			
			float textWidth = textInfo.width;
			float textHeight = textInfo.height -desc/*+ 1*/; // 保证能绘制文本高度和加粗效果(tly=ury; +1, tyl=0; -desc)
			
			float cellContentWidth = cellDrawWidth - cell.leftBorderWidth - cell.rightBorderWidth;
			float cellContentHeight = cellDrawHeight - cell.topBorderWidth - cell.bottomBorderWidth;
			if (cellContentHeight > textHeight) {
	    		switch (cell.getVerticalAlignment()) {
	    			case Rectangle.ALIGN_MIDDLE : {
	    				tly += (cellContentHeight - textHeight) / 2f;
	    				break;
	    			}
	    			case Rectangle.ALIGN_TOP : {
	    				tly += (cellContentHeight - textHeight);
	    				break;
	    			}
	    			default : {
	    				break;
	    			}
	    		}
			} else {
				tly = -(textHeight - cellContentHeight); // top对齐
				
				// 此种情况Excel对垂直无效果,按top显示
				if (cell.getVerticalAlignment() == Rectangle.ALIGN_BOTTOM) {
					tly += -desc;
				}
			}
			
			if (displayTextDrawBorder) {
    			canvas.saveState();
    			canvas.setColorStroke(BaseColor.BLUE);
    			canvas.rectangle(0, 0, textWidth, textHeight);
    			canvas.stroke();
    			canvas.restoreState(); // 开始的位置
			}
			
			// 对应Excel效果
			float textClipWidth = cellContentWidth;
			if (cellContentWidth < textInfo.width) {
				textClipWidth = textInfo.width; // 这样水平对齐就无意义, 不然会按对齐显示
			}
			
			PdfTemplate fontTemplate = canvas.createTemplate(textClipWidth, textHeight);
			ColumnText ct = new ColumnText(fontTemplate);
			ct.setSimpleColumn(phrase, 
			                   0, 0, textClipWidth, textHeight/* - desc*/, 
			                   leading, 
			                   cell.getHorizontalAlignment());
			ct.go();
			
			if (displayTextDrawBorder) {
    			fontTemplate.saveState();
    			fontTemplate.setLineDash(3f, 3f);
    			fontTemplate.setColorStroke(BaseColor.BLUE);
    			fontTemplate.rectangle(0, 0, textWidth, textHeight);
    			fontTemplate.stroke();
    			fontTemplate.restoreState(); // 移动到cellTemplate中再被移动到最外层的位置
			}
			
			PdfTemplate cellTemplate = canvas.createTemplate(textClipWidth, cellContentHeight);
			cellTemplate.addTemplate(fontTemplate, 0, tly);  // addTemplate是向上绘制
			
			if (displayTextDrawBorder) {
    			cellTemplate.saveState();
    			cellTemplate.setLineDash(3f, 3f);
    			cellTemplate.setColorStroke(BaseColor.GREEN);
    			cellTemplate.rectangle(0, 0, cellContentWidth, cellContentHeight);
    			cellTemplate.stroke();
    			cellTemplate.restoreState(); // 移动到clipTemplate中再被移动到最外层的位置
			}

			// 对应Excel效果
			float x0 = 0;
			if (cellContentWidth >= textInfo.width) {
				x0 = cell.drawCompleteWidthXPos == 0 ? 0 : cell.drawCompleteWidthXPos - cell.leftBorderWidth;
			} else {
	    		switch (cell.getHorizontalAlignment()) {
	    			case Rectangle.ALIGN_CENTER : {
	    				float offset = (textInfo.width - cellContentWidth) / 2f;
	    				x0 = cell.drawCompleteWidthXPos == 0 ? offset : cell.drawCompleteWidthXPos - cell.leftBorderWidth + offset;
	    				break;
	    			}
	    			case Rectangle.ALIGN_RIGHT : {
	    				float offset = (textInfo.width - cellContentWidth);
	    				x0 = cell.drawCompleteWidthXPos == 0 ? offset : cell.drawCompleteWidthXPos - cell.leftBorderWidth + offset;
	    				break;
	    			}
	    			default : {
	    				x0 = cell.drawCompleteWidthXPos == 0 ? 0 : cell.drawCompleteWidthXPos - cell.leftBorderWidth;
	    				break;
	    			}
	    		}
			}

			float y0 = cellContentHeight - cell.drawCompleteHeight - rect.drawHeight;
			float drawWidth = rect.drawWidth - rect.drawBorderWidthLeft - rect.drawBorderWidthRight;
			float drawHeight = rect.drawHeight;
			PdfTemplate clipTemplate = canvas.createTemplate(drawWidth, drawHeight);
			clipTemplate.addTemplate(cellTemplate, -(x0), -(y0)); // x:绘制位置减起始位置, y:绘制位置
			
			if (displayTextDrawBorder) {
    			clipTemplate.saveState();
    			clipTemplate.setLineDash(3f, 3f);
    			clipTemplate.setColorStroke(BaseColor.PINK);
    			clipTemplate.rectangle(0, 0, rect.drawWidth, rect.drawHeight);
    			clipTemplate.stroke();
    			clipTemplate.restoreState(); // clip后的内容
			}
			
			return clipTemplate;
		}
		
		/**
		 * 处理文本内容信息
		 * @param obj
		 * @param textInfo
		 */
		@SuppressWarnings("rawtypes")
		public void processTextInfo(Object obj, PdfTextInfo textInfo) {
			if (obj instanceof Chunk) {
				Chunk chunk = (Chunk) obj;
				BaseFont bf = chunk.getFont().getBaseFont();
				
				// chunk.getFont().getSize()==30,chunk.getFont().getBaseFont().getSize()==undefined
				float fontSize = textInfo.fontSize;
				// f1.getBaseFont().getFontDescriptor(BaseFont.ASCENT, fontSize)中文返回0, 英文返回实际绘制内容大小不包含行间隙
				float asc = bf.getFontDescriptor(BaseFont.ASCENT, fontSize);
				float desc = bf.getFontDescriptor(BaseFont.DESCENT, fontSize);
				float textLeading = asc - desc;
				
				if (textLeading > textInfo.leading) {
					textInfo.leading = textLeading;
				}
				
				String chunkContent = chunk.getContent();
				
				float[] wh = getContentWH(chunkContent, bf, fontSize, textLeading);
				
				float w = wh[0];
				float h = wh[1];
				float rn = wh[2];

				if (textInfo.isEnterEnd) {
					if (w > textInfo.width) {
						textInfo.width = w;
					}
				} else if (chunkContent.startsWith("\n") || chunkContent.startsWith("\r\n") || chunkContent.startsWith("\r")){
					if (w > textInfo.width) {
						textInfo.width = w;
					}
				} else {
					// 拼接内容=前一段最后部分内容+当前最开始部分内容
					float currentStartWidth = 0;
					if (rn == 1) {
						currentStartWidth = w;
					} else {
						int idx = 0;
						String currentStartText = null;
						if ((idx = chunkContent.indexOf('\n')) > -1) {
							currentStartText = chunkContent.substring(0, idx);
						} else if ((idx = chunkContent.indexOf("\r\n")) > -1) {
							currentStartText = chunkContent.substring(0, idx);
						} else if ((idx = chunkContent.indexOf('\r')) > -1) {
							currentStartText = chunkContent.substring(0, idx);
						}
						
						if (currentStartText.length() > 0) {
							currentStartWidth = bf.getWidthPoint(currentStartText, fontSize);
						}
					}
					
					float width = textInfo.lineWidth + currentStartWidth;
					if (width > w) {
						w = width;
					}
					if (w > textInfo.width) {
						textInfo.width = w;
					}
					textInfo.lineWidth = width;
				}
				
				if (rn == 1) {
					if (textInfo.lineHeight < textLeading) {
						textInfo.height = textInfo.height - textInfo.lineHeight + textLeading;
						textInfo.lineHeight = textLeading;
					}
				} else {
					if (textInfo.lineHeight < textLeading) {
						textInfo.height = textInfo.height - textInfo.lineHeight + textLeading + (h - textLeading);
					} else {
						textInfo.height = textInfo.height + (h - textLeading);
					}
					textInfo.lineHeight = textLeading;
				}
				
				if (chunkContent.endsWith("\n") || chunkContent.endsWith("\r\n") || chunkContent.endsWith("\r")) {
					textInfo.isEnterEnd = true;
					textInfo.lineWidth = 0;
					textInfo.lineHeight = textLeading;
				} else {
					textInfo.isEnterEnd = false;
					if (rn == 1) {
						textInfo.lineWidth = w;
					} else {
						int idx = 0;
						String currentLastText = null;
						if ((idx = chunkContent.lastIndexOf('\n')) > -1) {
							currentLastText = chunkContent.substring(idx + 1);
						} else if ((idx = chunkContent.lastIndexOf("\r\n")) > -1) {
							currentLastText = chunkContent.substring(idx + 2);
						} else if ((idx = chunkContent.lastIndexOf('\r')) > -1) {
							currentLastText = chunkContent.substring(idx + 1);
						}
						
						if (currentLastText.startsWith("\n") || currentLastText.startsWith("\r\n") || currentLastText.startsWith("\r")) {
							textInfo.lineWidth = 0;
						} else {
							textInfo.lineWidth = bf.getWidthPoint(currentLastText, fontSize);
						}
					}
				}
			} else if (obj instanceof List) {
				List list = (List) obj;
				for (int i = 0, j = list.size(); i < j; i++) {
					processTextInfo(list.get(i), textInfo);
				}
			}
		}
		
		/**
		 * 获得文本内容宽度和高度
		 * @param str 文本内容
		 * @param bf 基字体
		 * @param fontSize 字体尺寸
    	 * @param leading 字体实际高度(The ascent is the space needed by a glyph above the baseline, and the descent is the space below the baseline.)
    	 * @return float[] 0:maxWidth, 1:totalHeight, 2:rows
		 */
		public float[] getContentWH(String str, BaseFont bf, float fontSize, float leading) {
			float[] result = new float[3];
			float maxWidth = 0;
			int n = 1;
			int beginIndex = 0;
			int endIndex = 0;
			int currentIndex = 0;
			do {
				if ((endIndex = str.indexOf('\n', currentIndex)) > -1) {
					String subStr = str.substring(beginIndex, endIndex);
					float w = bf.getWidthPoint(subStr, fontSize);
					if (w > maxWidth) {
						maxWidth = w;
					}
					++n;
					currentIndex = endIndex + 1;
					beginIndex = currentIndex;
				} else if ((endIndex = str.indexOf("\r\n", currentIndex)) > -1) {
					String subStr = str.substring(beginIndex, endIndex);
					float w = bf.getWidthPoint(subStr, fontSize);
					if (w > maxWidth) {
						maxWidth = w;
					}
					++n;
					currentIndex = endIndex + 2;
					beginIndex = currentIndex;
				} else if ((endIndex = str.indexOf('\r', currentIndex)) > -1) {
					String subStr = str.substring(beginIndex, endIndex);
					float w = bf.getWidthPoint(subStr, fontSize);
					if (w > maxWidth) {
						maxWidth = w;
					}
					++n;
					currentIndex = endIndex + 1;
					beginIndex = currentIndex;
				} else {
					break;
				}
			} while(true);
			
			if (beginIndex == 0 && endIndex == -1) {
				float w = bf.getWidthPoint(str, fontSize);
				if (w > maxWidth) {
					maxWidth = w;
				}
			} else if (beginIndex < str.length()) {
				String subStr = str.substring(beginIndex);
				float w = bf.getWidthPoint(subStr, fontSize);
				if (w > maxWidth) {
					maxWidth = w;
				}
			}
			
			// 只要使用getFontDescriptor获得leading,那么每行字体之间都会有一个顶端间隙,这些间隙就是字体绘制过程中顶部的部分,所以不需要设置额外的大小
			result[0] = maxWidth;
			result[1] = n * leading;
			result[2] = n;
			
			return result;
		}
		
		/**
		 * 设置单元格文本内容绘制方式. 要求格子有高宽和文本控制设置
		 * @param cell
		 */
		public void setTextDrawControl(PdfTableCell cell) {
			if (cell.textControl == null) {
				cell.textControl = TextControlEnum.CLIP;
				cell.textDrawControl = TextControlEnum.DIRECT;
				return;
			}
			
			PdfTextInfo textInfo = cell.textInfo;
			if (textInfo == null) {
				textInfo = getPdfTextInfo(cell, cell.getTextPhrase());
			}
			float drawWidth = cell.maxWidth;
			float drawHeight = cell.getMaxHeight();
			float cellContentWidth = drawWidth - cell.leftBorderWidth - cell.rightBorderWidth;
			float cellContentHeight = drawHeight - cell.topBorderWidth - cell.bottomBorderWidth;
			switch (cell.textControl) {
				case CLIP : {
					if (textInfo.width > cellContentWidth || textInfo.height > cellContentHeight) {
						cell.textDrawControl = TextControlEnum.CLIP;
					} else {
						cell.textDrawControl = TextControlEnum.DIRECT;
					}
					break;
				}
				case STRETCH : {
					if (textInfo.width <= cellContentWidth && textInfo.height <= cellContentHeight) {
						cell.textDrawControl = TextControlEnum.DIRECT;
					} else if (textInfo.width <= cellContentWidth) {
						if (textInfo.height <= cellContentHeight) {
							cell.textDrawControl = TextControlEnum.DIRECT;
						} else {
							cell.textDrawControl = TextControlEnum.CLIP;
						}
					} else {
						setAutoSize(cell, textInfo, cellContentWidth, cellContentHeight, drawWidth, drawHeight);
					}
						
					break;
				}
				case SHRINK : {
					if (textInfo.width > cellContentWidth) {
						// 改变字体大小, 使其内容高宽都能容纳在格子中
						float wf = cellContentWidth / textInfo.width;
						if (textInfo.height * wf > cellContentHeight) {
							float hf = cellContentHeight / (textInfo.height * wf);
							wf = wf * hf;
						}
						wf -= 0.001f;
						if (wf < 0) {
							wf = 0;
						}
						
						PdfTextInfo newTextInfo = new PdfTextInfo();
						newTextInfo.fontSize = textInfo.fontSize * wf;
						Phrase phrase = cell.getTextPhrase();
						processTextInfo(phrase.getChunks(), newTextInfo);
						
						String text = phrase.getContent();
						cell.fontSelector.setFontsSize(newTextInfo.fontSize);
						phrase = cell.fontSelector.process(text);
						cell.setAndSavePhrase(phrase);
						
						cell.textInfo = newTextInfo;
						cell.textDrawControl = TextControlEnum.DIRECT;
					} else if (textInfo.height > cellContentHeight) {
						cell.textDrawControl = TextControlEnum.CLIP;
					} else {
						cell.textDrawControl = TextControlEnum.DIRECT;
					}
					break;
				}
				case AUTOSIZE : {
					if (textInfo.width <= cellContentWidth && textInfo.height <= cellContentHeight) {
						cell.textDrawControl = TextControlEnum.DIRECT;
					} else {
						setAutoSize(cell, textInfo, cellContentWidth, cellContentHeight, drawWidth, drawHeight);
					}
					break;
				}
				default : {
					cell.textDrawControl = TextControlEnum.DIRECT;
					break;
				}
			}
		}
		
		private void setAutoSize(PdfTableCell cell, PdfTextInfo textInfo, float cellContentWidth, float cellContentHeight, float drawWidth, float drawHeight) {
			// 格子宽度不变, 用现有文本内容计算自动换行后需要的高度, 然后比较格子现高度是否小于计算的文本内容高度, 如果小于就改变格子的高度.
			float newHeight = calculateTextHeight(cell, cellContentWidth, true);
			
			textInfo.height = newHeight;
			if (newHeight > cellContentHeight) {
				float desc = cell.fontSelector.getFont(0).getBaseFont().getFontDescriptor(BaseFont.DESCENT, textInfo.fontSize);
				drawHeight = drawHeight - cellContentHeight + newHeight - desc; // 增加desc使得格子内容不紧凑
				cell.setFixedHeight(drawHeight);
			}
			
			// ColumnText#go默认就是按换行处理
			Font font = cell.fontSelector.getFont(0);
			float minWidth = font.getBaseFont().getWidthPoint('W', font.getSize());
			if (minWidth < drawWidth) {
				cell.textDrawControl = TextControlEnum.DIRECT;
			} else {
				cell.textDrawControl = TextControlEnum.CLIP;
			}
		}
		
		/**
		 * 给定宽度计算指定文本内容需要的高度
		 * @param textInfo
		 * @param phrase
		 * @param limitWidth
		 * @return
		 */
		public float calculateTextHeight(PdfTableCell cell, float limitWidth, boolean isInsertEnter) {
			PdfTextInfo textInfo = cell.textInfo;
			PdfTextInfo newTextInfo = new PdfTextInfo();
			newTextInfo.fontSize = textInfo.fontSize;
			newTextInfo.leading = textInfo.leading;
			
			Phrase phrase = cell.getTextPhrase();
			
			StringBuilder stringBuilder = null;
			if (isInsertEnter) {
				stringBuilder = new StringBuilder();
			}
			calculateTextHeight(phrase.getChunks(), newTextInfo, limitWidth, stringBuilder);
			if (stringBuilder != null) {
				String text = stringBuilder.toString();
				//cell.fontSelector.setFontsSize(newTextInfo.fontSize);
				phrase = cell.fontSelector.process(text);
				cell.setAndSavePhrase(phrase);
				
				newTextInfo = new PdfTextInfo();
				newTextInfo.fontSize = textInfo.fontSize;
				processTextInfo(phrase.getChunks(), newTextInfo);
				cell.textInfo = newTextInfo;
			}
			
			return newTextInfo.height;
		}
		
		@SuppressWarnings("rawtypes")
		public void calculateTextHeight(Object obj, PdfTextInfo textInfo, float limitWidth, StringBuilder stringBuilder) {
			if (obj instanceof Chunk) {
				Chunk chunk = (Chunk) obj;
				BaseFont bf = chunk.getFont().getBaseFont();
				
				float fontSize = textInfo.fontSize;
				//float asc = bf.getFontDescriptor(BaseFont.ASCENT, fontSize);
				//float desc = bf.getFontDescriptor(BaseFont.DESCENT, fontSize);
				// 简单使用按最大的leading计算
				float textLeading = textInfo.leading;
				
				if (textInfo.height == 0) {
					textInfo.height = textLeading;
				}

				String chunkContent = chunk.getContent();
				float currentWidth = textInfo.lineWidth;
				for (int i = 0, j = chunkContent.length(); i < j; ++i) {
					//String str = String.valueOf(chunkContent.charAt(i));
					char c = chunkContent.charAt(i);
					
					if (c == '\n') {
						currentWidth = 0;
						textInfo.height += textLeading;
					} else {
						float cw = bf.getWidthPoint(c, fontSize);
						if (currentWidth + cw > limitWidth) {
							currentWidth = cw;
							textInfo.height += textLeading;
							
							if (stringBuilder != null && stringBuilder.length() > 0) {
								stringBuilder.append('\n');
							}
						} else {
							currentWidth += cw;
						}
					}
					
					if (stringBuilder != null) {
						stringBuilder.append(c);
					}
				}
				
				textInfo.lineWidth = currentWidth;

			} else if (obj instanceof List) {
				List list = (List) obj;
				for (int i = 0, j = list.size(); i < j; i++) {
					calculateTextHeight(list.get(i), textInfo, limitWidth, stringBuilder);
				}
			}
		}
		
		/**
		 * 绘制边框和单元格底纹填充
		 * @param cell
		 * @param rect
		 * @param canvases
		 */
		public void writeBorderAndBackground(PdfTableCell cell, DrawRectangle rect, PdfContentByte[] canvases) {
			// 颜色填充都是满格, 只要先填充背景再画边框就行
			BaseColor background = cell.getBackgroundColor();
			if (background != null) {
				PdfContentByte backgr = canvases[PdfPTable.BACKGROUNDCANVAS];
				backgr.saveState();
				backgr.setColorFill(background);
				
				backgr.rectangle(rect.getLeft(), rect.getTop(), rect.getDrawWidth() + 0.1f, -rect.getDrawHeight() - 0.1f);
				
				backgr.fill();
				backgr.restoreState();
			}
			
			if (rect.hasDrawBorder() && cell.getCellEvent() instanceof BorderRoundRectangle) {
				BorderRoundRectangle borderRoundRectangle = (BorderRoundRectangle) cell.getCellEvent();

				BorderRoundRectangle drawBorder = new BorderRoundRectangle();
				
				if (rect.hasBorder(Rectangle.TOP) && borderRoundRectangle.getTopBorder() != null) {
					PdfBorderStyle style = borderRoundRectangle.getTopBorder();
					drawBorder.setTopBorder(style);
					rect.setDrawBorderWidthTop(style.getBorderWidth());
				}
				
				if (rect.hasBorder(Rectangle.LEFT) && borderRoundRectangle.getLeftBorder() != null) {
					PdfBorderStyle style = borderRoundRectangle.getLeftBorder();
					drawBorder.setLeftBorder(style);
					rect.setDrawBorderWidthLeft(style.getBorderWidth());
				}
				
				if (rect.hasBorder(Rectangle.RIGHT) && borderRoundRectangle.getRightBorder() != null) {
					PdfBorderStyle style = borderRoundRectangle.getRightBorder();
					drawBorder.setRightBorder(style);
					rect.setDrawBorderWidthRight(style.getBorderWidth());
				}
				
				if (rect.hasBorder(Rectangle.BOTTOM) && borderRoundRectangle.getBottomBorder() != null) {
					PdfBorderStyle style = borderRoundRectangle.getBottomBorder();
					drawBorder.setBottomBorder(style);
					rect.setDrawBorderWidthBottom(style.getBorderWidth());
				}
				
				drawBorder.cellLayout(cell, rect, canvases);
			}
		}
		
		/**
		 * 绘制图片
		 * @param cell
		 * @param rect
		 * @param canvases
		 * @param zoom
		 */
		public void writeImage(PdfTableCell cell, DrawRectangle rect, PdfContentByte[] canvases, float zoom) {
			Image image = cell.getImage();
			if (image == null) {
				return;
			}
			
			// 不缩放. 旋转,水印等不需要处理
			PdfContentByte textCanvas = canvases[PdfPTable.TEXTCANVAS];
			
			try {
				//zoomImage(cell, (PdfImage) image);
				
				// 边框问题
				float imgWidth = rect.drawWidth - rect.getDrawBorderWidthLeft() - rect.getDrawBorderWidthRight();
				if (imgWidth < 1) {
					return;
				}
				float imgHeight = rect.drawHeight - rect.getDrawBorderWidthTop() - rect.getDrawBorderWidthBottom();
				if (imgHeight < 1) {
					return;
				}
				rect = DrawRectangle.copyBorder(rect);
				rect.drawWidth = imgWidth;
				rect.drawHeight = imgHeight;
				rect.drawXPos = rect.drawXPos + rect.getDrawBorderWidthLeft();
				rect.drawYPos = rect.drawYPos + rect.getDrawBorderWidthTop();
				rect.setLeft(rect.getLeft() + rect.getDrawBorderWidthLeft());
				rect.setTop(rect.getTop() - rect.getDrawBorderWidthTop());
				
				image = clipImage(cell, rect, (PdfImage) image, zoom);
				
				float imageYPos = rect.getTop();
				if (image.getScaledHeight() < rect.drawHeight) {
					imageYPos -= image.getScaledHeight();
				} else {
					imageYPos -= rect.drawHeight;
				}
				
				// use addImage(image, image_width, 0, 0, image_height, x, y).
				textCanvas.addImage(image, 
				                    image.getScaledWidth(), 0, 0, 
				                    image.getScaledHeight(), 
				                    rect.getLeft(), 
				                    imageYPos);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		/*
		 * 单位转换因子.
		 * PDF绘制最小单位是pt,HTML和Excel中使用单位是px,字体使用pt,所以绘制PDF时需要一个单位的转换. 
		 * 假设windows使用96dpi为显示单位, 则 96px = 1in, 72pt = 1in => 1px = 0.75pt
		 */
		private Image clipImage(PdfTableCell cell, DrawRectangle rect, Image image, float zoom) throws Exception {
			if (zoom == 0) {
				zoom = 1;
			}
			float w = rect.drawWidth / zoom;
			float h = rect.drawHeight / zoom;
			
			if (checkImageSize(image, w, h)) {
				zoomImage(image, zoom);
				return image;
			}

			net.coobird.thumbnailator.geometry.Position position = new PdfPosition((int) (cell.drawCompleteWidthXPos / zoom), 
			                                                                       (int) (cell.drawCompleteHeight / zoom));

			image = clipImage((PdfImage) image, position, ((int) w) + 1, ((int) h) + 1);
			
			zoomImage(image, zoom);
			
			return image;
		}
		
		private Image clipImage(PdfFloating floating, Image image, float zoom) throws Exception {
			if (zoom == 0) {
				zoom = 1;
			}
			DrawRectangle rect = floating.rect;
			float w = rect.drawWidth / zoom;
			float h = rect.drawHeight / zoom;
			
			if (checkImageSize(image, w, h)) {
				zoomImage(image, zoom);
				return image;
			}
			
			net.coobird.thumbnailator.geometry.Position position = new PdfPosition((int) ((floating.drawXPos - floating.cell.xPos) / zoom), 
			                                                                       (int) ((floating.drawYPos - floating.cell.yPos) / zoom));

			image = clipImage((PdfImage) image, position, ((int) w) + 1, ((int) h) + 1);
			
			zoomImage(image, zoom);
			
			return image;
		}
		
		private Image clipImage(PdfImage image, net.coobird.thumbnailator.geometry.Position position, int width, int height) throws Exception {
			ByteArrayOutputStream out = new ByteArrayOutputStream();

			byte[] b = ((PdfImage) image).getImgBytes();

			// 1 unit = 1/72 in, assume windows 96dpi = 1 in, then 1 px = 0.75 pt
			Thumbnails.of(new ByteArrayInputStream(b))
		        .sourceRegion(position, width, height)
		        .size(width, height)
		        //.outputQuality(1)
		        .toOutputStream(out);
			
			Image newImage = Image.getInstance(out.toByteArray());
			
			// scaleToFit的效果将不失真, Thumbnails.of().scale()失真
			//newImage.scaleToFit(width * 0.75f,  height * 0.75f);
			//zoomImage(newImage, UNIT_PT_TO_PX_FAC);
			
			out.close();
			
			return newImage;
		}
		
		private void zoomImage(Image image, float fac) throws Exception {
			if (fac == 1.0f) {
				return;
			}
    		image.scaleToFit(image.getScaledWidth() * fac, image.getScaledHeight() * fac);
    		//image.scalePercent(fac);
	    }
		
		private boolean checkImageSize(Image image, float drawWidth, float drawHeight) {
			return ( (int) (image.getScaledWidth()) <= (int) drawWidth
					&& (int) (image.getScaledHeight()) <= (int) drawHeight);
		}
		
		private void writeSplitRowCells(Integer pageNum, PdfPageInfo pageInfo, PdfTableCellInfo cellInfo,
										   List<PdfTableCell> splitRowMapping) throws DocumentException, IOException {
			PdfTableCell firstCell = splitRowMapping.get(0);
			if (firstCell.nextSplitPageNum == pageNum.intValue()) {
				
				PdfContentByte[] canvases = pageInfo.getWritingRows(pageNum);
				
				List<PdfTableCell> list = new ArrayList<PdfTableCell>();
				for (PdfTableCell cell : splitRowMapping) {
					if (cell.drawSplitHeight + cell.drawSplitHeightOffset > pageInfo.onePageFitHeight) {
						float lastDrawHeight = cell.drawSplitHeight + cell.drawSplitHeightOffset - pageInfo.onePageFitHeight;
						float drawHeight = cell.drawSplitHeight - lastDrawHeight;
						cell.drawSplitHeight = drawHeight;
						
						writeRowCell(cell, pageInfo, cellInfo, canvases, false);
						
						list.add(cell);
						cell.rowSplitDrawYPos = pageInfo.onePageFitHeight + pageInfo.bottomMargin;
						
						cell.drawSplitHeight = lastDrawHeight;
						
						cell.drawCompleteHeight += drawHeight; // pageInfo.onePageFitHeight;
					} else {
						writeRowCell(cell, pageInfo, cellInfo, canvases, true);
						
						cell.drawCompleteHeight = cell.getMaxHeight();
					}
				}
				
				pageInfo.endWritingRows(canvases);
				
				if (splitRowMapping.size() != list.size()) {
					splitRowMapping.clear();
					for (int i = 0, j = list.size(); i < j; i++) {
						PdfTableCell cell = list.get(i);
						cell.nextSplitPageNum++;
						splitRowMapping.add(cell);
					}
				} else {  // $Date: 2017-11-30 - 没有绘制完毕nextSplitPageNum属性也要+1
					for (PdfTableCell cell : splitRowMapping) {
						cell.nextSplitPageNum++;
					}
				}
			}
		}

		/**
		 * 写入一行格子到画布
		 * 
		 * @param cell
		 * @param pageInfo
		 * @param cellInfo
		 * @param canvases
		 * @param isDrawBottomBorder
		 * @throws DocumentException
		 * @throws IOException
		 */
		public void writeRowCell(PdfTableCell cell, PdfPageInfo pageInfo, PdfTableCellInfo cellInfo, PdfContentByte[] canvases, boolean isDrawBottomBorder) throws DocumentException, IOException {
			float drawSplitWidth = cell.drawWidth;
			float drawSplitHeight = cell.drawSplitHeight;
			if (cell.hasColSplit) {
				drawSplitWidth = cell.drawSplitWidth;
			}
			float llx = cell.rowSplitDrawXPos;
			float lly = cell.rowSplitDrawYPos - drawSplitHeight;
			float urx = llx + drawSplitWidth;
			float ury = cell.rowSplitDrawYPos;
			float w = drawSplitWidth;
			float h = drawSplitHeight;
			DrawRectangle rect = new DrawRectangle(llx, lly, urx, ury, llx, cell.rowSplitDrawYPos, w, h);
			
			if (cell.isHeader) {
				rect.setBorder(DrawRectangle.BOX);
				if (cell.hasColSplit) {
					if (cell.drawCompleteWidth > 0 && w == pageInfo.onePageFitWidth) {
						rect.drawBorderForceLeft = true;
						rect.drawBorderForceRight = true;
					} else if (cell.drawCompleteWidth > 0 && cell.drawCompleteWidth + w == cell.maxWidth) {
						rect.drawBorderForceLeft = true;
					} else {
						rect.drawBorderForceRight = true;
					}
				}
			} else {
				if (cell.hasColSplit) {
					float xPos = cellInfo.calculateXPos(cell);
					if (cell.drawCompleteWidth == cell.maxWidth) {
						//rect.setBorder(DrawRectangle.RIGHT);
						
						// $Date: 2016-12-8 - 新需求要画上左边框
						rect.setBorder(DrawRectangle.LEFT + DrawRectangle.RIGHT);
						rect.drawBorderForceLeft = true;
					} else if (cell.rowSplitDrawXPos == xPos + pageInfo.leftMargin && cell.drawCompleteWidthXPos == 0){
						//rect.setBorder(DrawRectangle.LEFT);
						
						// $Date: 2016-12-8 - 新需求要画上右边框
						rect.setBorder(DrawRectangle.LEFT + DrawRectangle.RIGHT);
						rect.drawBorderForceRight = true;
					} else {
						rect.setBorder(DrawRectangle.LEFT + DrawRectangle.RIGHT);
						rect.drawBorderForceLeft = true;
						rect.drawBorderForceRight = true;
					}
				} else {
					if (cell.hasRowColSplit) {
						//rect.setBorder(DrawRectangle.RIGHT);
						
						//$Date: 2016-12-8 - 新需求要画上左边框
						rect.setBorder(DrawRectangle.LEFT + DrawRectangle.RIGHT);
						rect.drawBorderForceLeft = true;
					} else {
					    rect.setBorder(DrawRectangle.LEFT + DrawRectangle.RIGHT);
					}
				}
				
				if (isDrawBottomBorder) {
					if (rect.getBorder() != Rectangle.UNDEFINED) {
						rect.setBorder(rect.getBorder() + DrawRectangle.BOTTOM);
					} else {
						rect.setBorder(DrawRectangle.BOTTOM);
					}
				}
			}

//			if (cell.hasColSplit) {
//				float xPos = cellInfo.calculateXPos(cell);
//				if (cell.drawCompleteWidth == cell.maxWidth) {
//					rect.setBorder(Rectangle.RIGHT);
//				} else if (cell.rowSplitDrawXPos == xPos + pageInfo.leftMargin && cell.drawCompleteWidthXPos == 0){
//					rect.setBorder(Rectangle.LEFT);
//				}
//			} else {
//				if (cell.hasRowColSplit) {
//					rect.setBorder(Rectangle.RIGHT);
//				} else {
//				    rect.setBorder(Rectangle.LEFT + Rectangle.RIGHT);
//				}
//			}
//			
//			if (isDrawBottomBorder) {
//				if (rect.getBorder() != Rectangle.UNDEFINED) {
//					rect.setBorder(rect.getBorder() + Rectangle.BOTTOM);
//				} else {
//					rect.setBorder(Rectangle.BOTTOM);
//				}
//			}

			writeBorderAndBackground(cell, rect, canvases);
			
			writeText(pageInfo.writer, cell, rect, canvases, false);
			
			if (cell.getImage() != null) {
				writeImage(cell, rect, canvases, cellInfo.imageZoom);
			}
		}
	}
	
	/**
	 * <code>PdfPHFEvent</code>控制页眉页脚的事件类. <br/>
	 * <p><blockquote><pre>
	 * <xml-fragment xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing" xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:main="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
     *     <main:oddHeader>&amp;L11&amp;C&amp;P--&amp;N--&amp;D--&amp;T--&amp;F--&amp;A&amp;R22</main:oddHeader>
     *     <main:oddFooter>&amp;C第 &amp;P 页，共 &amp;N 页</main:oddFooter>
	 * </xml-fragment>
	 * 
	 * &L 表示左边部分开始
	 * &C 表示中间部分开始
	 * &R 表示右边部分开始

	 * &P 表示 &[页码] 变数
	 * &N 表示 &[总页数] 变数,PDF处理需注意
	 * &D 表示 &[日期]
	 * &T 表示 &[时间]
	 * &F 表示 &[文件名]
	 * &A 表示 &[当前SHEET的标签名]
	 * &G 表示 &[图片] 图片不处理
	 * </pre></blockquote></p>
	 * 
	 */
	static class PdfPHFEvent extends PdfPageEventHelper {
		
		public static final String P = "&P";
		public static final String N = "&N";
		public static final String D = "&D";
		public static final String T = "&T";
		public static final String F = "&F";
		public static final String A = "&A";
		//public static final String G = "&G";

		FontSelector fontSelector;
		float maxPHFWidth;
		float[] pageHRelativeWidths;
		float[] pageFRelativeWidths;
		float headerHeight;

		List<PdfTableCell> header;
		List<PdfTableCell> footer;

		// Constant
		String dateConstString;
		String timeConstString;
		String fileNameConstString;
		String sheetNameConstString;
		
		// Variable
		boolean hasPageNubmer;
		boolean hasPageTotalNumber;

		/**
		 * {@inheritDoc}
		 * 
		 * @see com.lowagie.text.pdf.PdfPageEventHelper#onEndPage(com.lowagie.text.pdf.PdfWriter, com.lowagie.text.Document)
		 */
		public void onEndPage(PdfWriter writer, Document document) {
			float xPos = document.left();
			float hYPos = document.top() + headerHeight; // Excel实际可以向下超过页眉/页脚区绘制内容
			float fYPos = document.bottom();
			
			Map<String, String> vars = new HashMap<String, String>(1);
			vars.put(P, Integer.toString(writer.getPageNumber()));
			
			PdfPTable pageHeader = getPdfPTable(maxPHFWidth, pageHRelativeWidths, header, fontSelector, vars);
			PdfPTable pageFooter = getPdfPTable(maxPHFWidth, pageFRelativeWidths, footer, fontSelector, vars);
			
			PdfContentByte canvas = writer.getDirectContent();
			if (pageHeader != null) {
				pageHeader.writeSelectedRows(0, -1, xPos, hYPos, canvas);
			}
			if (pageFooter != null) {
				pageFooter.writeSelectedRows(0, -1, xPos, fYPos, canvas);
			}
		}

		/**
		 * PDF中绘制总页数的方法只有2种. 参考<<iText in Action 2nd Edition>>, P152,175 <br/>
		 * 1: 使用PdfTemplate, 缺点是对于总页数很多时无法确定页号宽度.<br/>
		 * 2: 先不写入有总页数的部分, 等文档生成完毕后再使用PdfStamper处理一次PDF文档, 缺点是需要额外的内存.<br/>
		 * 其他: 通过先分析页面总页数, 之后直接使用这个分析的值.<br/>
		 * @param document
		 * @param canvas
		 */
		public void drawPHF(Document document, PdfContentByte canvas, int pageNumber, int pageTotalNumber) {
			float xPos = document.left();
			float hYPos = document.top() + headerHeight; // Excel实际可以向下超过页眉/页脚区绘制内容
			float fYPos = document.bottom();
			
			Map<String, String> vars = new HashMap<String, String>(2);
			vars.put(P, Integer.toString(pageNumber));
			vars.put(N, Integer.toString(pageTotalNumber));
			
			PdfPTable pageHeader = getPdfPTable(maxPHFWidth, pageHRelativeWidths, header, fontSelector, vars);
			PdfPTable pageFooter = getPdfPTable(maxPHFWidth, pageFRelativeWidths, footer, fontSelector, vars);

			if (pageHeader != null) {
				pageHeader.writeSelectedRows(0, -1, xPos, hYPos, canvas);
			}
			if (pageFooter != null) {
				pageFooter.writeSelectedRows(0, -1, xPos, fYPos, canvas);
			}
		}
		
		private PdfPTable getPdfPTable(float totalWidth, float[] relativeWidths, List<PdfTableCell> list, FontSelector selector, Map<String, String> vars) {
			if (list == null || list.size() == 0) {
				return null;
			}
			
			PdfPTable table = new PdfPTable(relativeWidths);
			table.setLockedWidth(true);
			table.setTotalWidth(totalWidth);
			
			for (PdfTableCell cell : list) {
				String text = cell.getTextPhrase().getContent();
				
				if (cell.containsAttr(P)) {
					text = text.replaceAll(P, getReplaceText(P, vars));
				}
				if (cell.containsAttr(N)) {
					text = text.replaceAll(N, getReplaceText(N, vars));
				}
				
				PdfTableCell newCell = PdfTableCell.copyCell(cell);
				newCell.setPhrase(selector.process(text));
				table.addCell(newCell);
			}
			
			return table;
		}
		
		private String getReplaceText(String key, Map<String, String> map) {
			String value = null;
			if (map != null && map.size() > 0) {
				value = map.get(key);
			}
			return value == null ? "" : value;
		}

		public static boolean hasPageNubmer(String text) {
			if (text == null || text.length() == 0) {
				return false;
			}
			
			return text.indexOf(P) > -1;
		}
		
		public static boolean hasPageTotalNubmer(String text) {
			if (text == null || text.length() == 0) {
				return false;
			}
			
			return text.indexOf(N) > -1;
		}
		
		public String renderConstString(String text) {
			if (text == null || text.length() == 0) {
				return "";
			}
			
			text = text.replaceAll(D, dateConstString);
			text = text.replaceAll(T, timeConstString);
			text = text.replaceAll(F, fileNameConstString);
			text = text.replaceAll(A, sheetNameConstString);
			
			return text;
		}
	}
	
	/**
	 * <code>ExcelWorkbook</code>封装97-2003,2007兼容操作的类.
	 */
	private static class ExcelWorkbook {
		
		private Workbook workbook;
		private DataFormatter formatter;
		private FormulaEvaluator evaluator;
		
		public ExcelWorkbook(Workbook workbook) {
			this.workbook = workbook;
			this.formatter = new DataFormatter();
			this.evaluator = createFormulaEvaluator();
		}

		public Sheet getSheet(String name) {
			return workbook.getSheet(name);
		}
		
		public FormulaEvaluator createFormulaEvaluator() {
			return workbook.getCreationHelper().createFormulaEvaluator();
		}
		
		/**
		 * 提取单元格的文本内容
		 * @see com.yss.sofa.report.engine#getExcelCellFormattedValue
		 * @param cell
		 * @return
		 */
		public String getExcelCellFormattedValue(ResultCell rc, Cell cell) {
			if (cell == null) {
				return "";
			}
			return (rc != null && rc.getDataType() == Const.CELL_DATA_TYPE_DATE) ? 
					formatter.formatCellValue(cell, evaluator).replaceAll("\"","") : formatter.formatCellValue(cell, evaluator);
		}
	}
	
	//==================================================
	// Customize PDF Table,Row,Cell Classes
	//==================================================
	/**
	 * <code>PdfTableCell</code>是PDF表格中的格子.
	 */
	static class PdfTableCell extends PdfPCell {
		int rowIndex;
		int colIndex;
		float maxWidth;
		Phrase textPhrase;
		
		float xPos;
		float yPos;

		float rowSplitDrawXPos;
		float rowSplitDrawYPos;
		float colSplitDrawXPos;
		boolean hasRowSplit;
		boolean hasColSplit;
		boolean hasRowColSplit;
		float drawWidth;
		float drawHeight;
		float drawSplitWidth;
		float drawSplitHeight;
		float drawCompleteWidth;
		float drawCompleteHeight;
		int nextSplitPageNum;
		
		// 每次绘制格子的起始坐标,格子左上角为(0,0)
		float drawCompleteWidthXPos;
		//float drawCompleteHeightYPos;
		
		PdfTextInfo textInfo;
		float topBorderWidth;
		float leftBorderWidth;
		float bottomBorderWidth;
		float rightBorderWidth;
		Map<String, Object> attr;
		
		// 过长或过大的文本内容是否进行自动换行,缩放填充,自动截取
		public PdfFontSelector fontSelector;
		public TextControlEnum textControl;
		public TextControlEnum textDrawControl;
		
		float originalXPos;
		float originalYPos;
		float originalWidth;
		float originalHeight;
		
		float drawSplitHeightOffset; // 插入表头的切分偏移量
		float drawCompletePreHeight;
		boolean isHeader;
		
		public PdfTableCell() {
			
		}

		public PdfTableCell(PdfTableCell cell){
			super(cell);
			this.rowIndex = cell.rowIndex;
			this.colIndex = cell.colIndex;
		}

		public PdfTableCell(PdfPTable table) {
			super(table);
		}
		
		public static PdfTableCell copyCell(PdfTableCell cell) {
			PdfTableCell newCell = null;
			if (cell.getImage() != null) {
				Image image = cell.getImage();
				cell.setImage(null);
				newCell = new PdfTableCell(cell);
				cell.setImage(image);
				newCell.setImage(image);
			} else {
				newCell = new PdfTableCell(cell);
			}
			newCell.rowIndex = cell.rowIndex;
			newCell.colIndex = cell.colIndex;
			newCell.maxWidth = cell.maxWidth;
			newCell.textPhrase = cell.textPhrase;
			
			newCell.xPos = cell.xPos;
			newCell.yPos = cell.yPos;

			newCell.rowSplitDrawXPos = cell.rowSplitDrawXPos;
			newCell.rowSplitDrawYPos = cell.rowSplitDrawYPos;
			newCell.colSplitDrawXPos = cell.colSplitDrawXPos;
			newCell.hasRowSplit = cell.hasRowSplit;
			newCell.hasColSplit = cell.hasColSplit;
			newCell.hasRowColSplit = cell.hasRowColSplit;
			newCell.drawWidth = cell.drawWidth;
			newCell.drawHeight = cell.drawHeight;
			newCell.drawSplitWidth = cell.drawSplitWidth;
			newCell.drawSplitHeight = cell.drawSplitHeight;
			newCell.drawCompleteWidth = cell.drawCompleteWidth;
			newCell.drawCompleteHeight = cell.drawCompleteHeight;
			newCell.nextSplitPageNum = cell.nextSplitPageNum;
			
			newCell.drawCompleteWidthXPos = cell.drawCompleteWidthXPos;
			//newCell.drawCompleteHeightYPos = cell.drawCompleteHeightYPos;
			
			newCell.textInfo = cell.textInfo;
			
			PdfPCellEvent cellEvent = cell.getCellEvent();
			if (cellEvent instanceof BorderRoundRectangle) {
				newCell.setCellEvent(null);
				BorderRoundRectangle borderRoundRectangle = (BorderRoundRectangle) cellEvent;
				newCell.setCellEvent(BorderRoundRectangle.copyBorder(borderRoundRectangle));
			}
			
			newCell.topBorderWidth = cell.topBorderWidth;
			newCell.leftBorderWidth = cell.leftBorderWidth;
			newCell.bottomBorderWidth = cell.bottomBorderWidth;
			newCell.rightBorderWidth = cell.rightBorderWidth;
			newCell.attr = cell.attr;
			
			newCell.fontSelector = cell.fontSelector;
			newCell.textControl = cell.textControl;
			newCell.textDrawControl = cell.textDrawControl;
			
			newCell.originalXPos = cell.originalXPos;
			newCell.originalYPos = cell.originalYPos;
			newCell.originalWidth = cell.originalWidth;
			newCell.originalHeight = cell.originalHeight;
			
			newCell.drawSplitHeightOffset = cell.drawSplitHeightOffset;
			newCell.drawCompletePreHeight = cell.drawCompletePreHeight;
			newCell.isHeader = cell.isHeader;
			
			return newCell;
		}
		
		public void setAndSavePhrase(Phrase phrase) {
			super.setPhrase(phrase);
			this.textPhrase = phrase;
		}
		
		public float getMaxHeight() {
			//return subRows != null ? maxHeight : this.getFixedHeight();
			return this.getFixedHeight();
		}
		
		public int getActualColspan() {
			//return subRows != null ? maxColspan : this.getColspan();
			return this.getColspan();
		}
		
		public Phrase getTextPhrase() {
			return textPhrase;
		}
		
		public void putAttr(String key, Object value) {
			if (attr == null) {
				attr = new HashMap<String, Object>();
			}
			attr.put(key, value);
		}
		
		public Object getAttr(String key) {
			if (attr == null) {
				return null;
			}
			return attr.get(key);
		}
		
		public boolean containsAttr(String key) {
			if (attr == null) {
				return false;
			}
			return attr.containsKey(key);
		}
		
		public Object removeAttr(String key) {
			if (attr == null) {
				return null;
			}
			return attr.remove(key);
		}
		
		public String toString() {
			String str = null;
			if (this.textPhrase != null) {
				str = this.textPhrase.getContent();
			} else if (this.getPhrase() != null) {
				str = this.getPhrase().getContent();
			}
			return str == null ? super.toString() : str;
		}
	}
	
	//==================================================
	// PDF Cells Information Classes
	//==================================================
	/**
	 * <code>PdfTableCellInfo</code>是PDF表格中所有格子的信息
	 */
	static class PdfTableCellInfo {
		/**
		 * 标题部分. 第一页开始部分.
		 */
		List<PdfTableCell> title;
		/**
		 * 顶端标题行. 第一页title下, 之后每页的开头(Excel页面设置->工作表) 对相应 fixed-header渲染器.
		 * fixed-header渲染器会让报表具有固定的表头，报表内容在竖向滚动时，表头不会随着内容滚动，类似于 excel中的冻结行。
		 * 在中模板需要配置 page 单元格的 title 和 header 属性。 title 和 header 属性分别表示报表的表头行数和列头
		 * 行数——注意是行数不是行号。 如 title=3， header=4，就表示报表的第 1 行~第 3 行是表头，第 4 行~第 7 行是列头，那么在 fixed-header 渲染器中，报表的前 7 行（ 3+4）都会被固定。
		 * 
		 * 在 sheet[main]中，结束符##现在可配置属性， ##{"title":2,"header":1}，
		 * 表示前 2 行为表头（只第 1页显示），第 3 行为列头（每页都显示），会影响到打印的结果，对预览结果无影响，若不配置，则全部作为明细数据；
		 */
		List<PdfTableCell> header;
		/**
		 * 表格中包含的主要格子<code>PdfTableCell</code>的集合
		 */
		List<PdfTableCell> detail;
		/**
		 * 最后一页最后一行下方的内容
		 */
		List<PdfTableCell> footer; //这个可忽略
		/**
		 * 浮动的对象.例如固定位置的二维码图片
		 */
		List<PdfTableCell> floating;
		/**
		 * 绘制使用的集合
		 */
		List<PdfTableCell> cells;
		/**
		 * 每个colspan对应的宽度
		 */
		float[] relativeWidths;
		/**
		 * 表格的宽度
		 */
		float totalWidth;
		/**
		 * 表格高度
		 */
		float totalHeight;
		/**
		 * 格子集合中每个格子的Y轴坐标.从左上角计算.
		 */
		Map<Integer, PdfTableCellPos> rowIndexPosMapping;
		/**
		 * 格子集合中每个格子的X轴坐标.从左上角计算.
		 */
		Map<Integer, PdfTableCellPos> colIndexPosMapping;
		/**
		 * 一页的高度
		 */
		float onePageFitHeight; // 计算使用: pageHeight - topMargin - bottomMargin;
		/**
		 * 格子的缩放比例
		 */
		float cellZoom;
		/**
		 * 图片的缩放比例
		 */
		float imageZoom;
		/**
		 * 字体的缩放比例
		 */
		float fontZoom;
		/**
		 * 行数
		 */
		int rowCount;
		/**
		 * 列数
		 */
		int colCount;
		
		/**
		 * 表头信息(可用于动态构建表头)
		 */
		HeaderInfo headerInfo;
		
		/**
		 * Excel关键信息
		 */
		ExcelCellInfo excelCellInfo;
		
		/**
		 * 构造函数. 所有格子集合应该按rowIndex和colIndex有序保存
		 * @param title 标题
		 * @param header 页眉
		 * @param detail 内容
		 * @param footer 页脚
		 * @param floating 浮动对象
		 * @param relativeWidths 格子的colspan对应的宽度
		 * @param strategy 处理格子信息的策略
		 */
		public PdfTableCellInfo(List<PdfTableCell> title,
		                        List<PdfTableCell> header,
		                        List<PdfTableCell> detail, 
		                        List<PdfTableCell> footer,
		                        List<PdfTableCell> floating,
		                        float[] relativeWidths,
		                        ExcelCellInfo excelCellInfo,
		                        DrawStrategy strategy) {
			this.title = title;
			this.header = header;
			this.detail = detail;
			this.footer = footer;
			this.floating = floating;
			this.cells = new ArrayList<PdfTableCell>();
			this.relativeWidths = relativeWidths;
			this.totalWidth = calculateTotalWidth();
			//this.totalHeight = calculateTotalHeight();
			this.onePageFitHeight = strategy.getDrawBodyHeight();
			this.rowIndexPosMapping = new HashMap<Integer, PdfTableCellPos>();
			this.colIndexPosMapping = new HashMap<Integer, PdfTableCellPos>();
			
			this.excelCellInfo = excelCellInfo;

			strategy.calculate(this);
		}
		
		/**
		 * 通过格子的colIndex找到格子的原始X坐标
		 * @param cell_
		 * @return
		 */
		public float calculateXPos(PdfTableCell cell_) {
			if (cell_.colIndex == 0) {
				return 0;
			}
			
			PdfTableCellPos pos = colIndexPosMapping.get(Integer.valueOf(cell_.colIndex));
			if (pos != null) {
				return pos.xPos;
			}
			
			float xPos = relativeWidths[0];
			for (int i = 1, j = cell_.colIndex; i < j; i++) {
				xPos += relativeWidths[i];
			}
			
			return xPos;
		}
		
		/**
		 * 通过格子的rowIndex找到格子的原始Y坐标
		 * @param cell_
		 * @return
		 */
		public float calculateYPos(PdfTableCell cell_) {
			if (cell_.rowIndex == 0) {
				return 0;
			}
			
			PdfTableCellPos pos = rowIndexPosMapping.get(Integer.valueOf(cell_.rowIndex));
			if (pos != null) {
				return pos.yPos;
			}
			
			int lastRowIndex = cell_.rowIndex;
			Map<Integer, PdfTableCellPos> map = new HashMap<Integer, PdfTableCellPos>();
			for (PdfTableCell cell : cells) {
				
				if (cell.rowIndex == lastRowIndex) {
					break;
				}
				
				float cellHeight = cell.getMaxHeight();
				Integer key = Integer.valueOf(cell.rowIndex);
				pos = map.get(key);
				if (pos == null) {
					pos = new PdfTableCellPos();
					pos.height = cellHeight;
					map.put(key, pos);
				} else if (pos.height > cellHeight) {
					pos.height = cellHeight;
				}
			}

			float yPos = 0;
			for (Iterator<PdfTableCellPos> iter = map.values().iterator(); iter.hasNext(); ) {
				pos = iter.next();
				
				yPos += pos.height;
			}
			
			return yPos;
		}
		
		/**
		 * 通过colIndex和colspan获得格子的宽度
		 * @param colIndex 格子的列索引
		 * @param colspan 格子占用的列数
		 * @return
		 */
		public float getCellWidth(int colIndex, int colspan) {
			float width = relativeWidths[colIndex];
			for (int i = 1; i < colspan; i++) {
				width += relativeWidths[colIndex + i];
			}
			return width;
		}
		
		/**
		 * 计算表格的宽度
		 * @return
		 */
		public float calculateTotalWidth() {
			if (relativeWidths == null || relativeWidths.length == 0) {
				return 0;
			}
			float width = 0;
			for (float w : relativeWidths) {
				width += w;
			}
			return width;
		}
		
		/**
		 * @return the cells
		 */
		public List<PdfTableCell> getCells() {
		
			return cells;
		}
		
		/**
		 * @param cells the cells to set
		 */
		public void setCells(List<PdfTableCell> cells) {
		
			this.cells = cells;
		}
		
		/**
		 * @return the relativeWidths
		 */
		public float[] getRelativeWidths() {
		
			return relativeWidths;
		}
		
		/**
		 * @param relativeWidths the relativeWidths to set
		 */
		public void setRelativeWidths(float[] relativeWidths) {
		
			this.relativeWidths = relativeWidths;
		}
		
		/**
		 * @return the totalWidth
		 */
		public float getTotalWidth() {
		
			return totalWidth;
		}
		
		/**
		 * @param totalWidth the totalWidth to set
		 */
		public void setTotalWidth(float totalWidth) {
		
			this.totalWidth = totalWidth;
		}
		
		/**
		 * @return the totalHeight
		 */
		public float getTotalHeight() {
		
			return totalHeight;
		}
		
		/**
		 * @param totalHeight the totalHeight to set
		 */
		public void setTotalHeight(float totalHeight) {
		
			this.totalHeight = totalHeight;
		}

	}
	
	/**
	 * <code>PdfTableCellPos</code>是PDF表格中格子的坐标信息
	 */
	static class PdfTableCellPos {
		PdfTableCell cell;
		float xPos;
		float yPos;
		float width;
		float height;
		float[] heightArray;
	}
	
	/**
	 * <code>HeaderInfo</code>是表头信息.用于构造动态表头
	 */
	static class HeaderInfo {
		int headerRowspan;
		float headerHeight;
		Map<Integer, List<PdfTableCell>> pageNumHeaderMapping;
	}
	
	/**
	 * <code>PdfPageInfo</code>是PDF页面信息
	 */
	static class PdfPageInfo {
		PdfWriter writer;
		Document document;
    	Rectangle pageSize;
    	PdfContentByte canvas;
    	PdfContentByte[] canvases;
    	float topMargin;
    	float leftMargin;
    	float bottomMargin;
    	float rightMargin;
    	float pageWidth;
    	float pageHeight;
    	float pageFitWidth;
    	float pageFitHeight;
    	float onePageFitWidth;
    	float onePageFitHeight;
    	float pageMaxFitHeight;
    	int pageWidthNum;
    	int pageHeightNum;
    	int pageHeightSplitNum;
    	Map<Integer, PdfPageInfo> canvasMapping;
    	
    	private PdfPageInfo() {}
    	
    	public PdfPageInfo(PdfWriter writer, Document document) {
    		this.writer = writer;
    		this.document = document;
    		this.pageSize = document.getPageSize();
    		this.topMargin = document.topMargin();
    		this.leftMargin = document.leftMargin();
    		this.bottomMargin = document.bottomMargin();
    		this.rightMargin = document.rightMargin();
    		this.pageWidth = pageSize.getWidth();
    		this.pageHeight = pageSize.getHeight();
    		this.pageFitWidth = toRoundHalfUpFloat(pageWidth - rightMargin, 2);
    		this.pageFitHeight = toRoundHalfUpFloat(pageHeight - bottomMargin, 2);
    		this.onePageFitWidth = toRoundHalfUpFloat(pageWidth - leftMargin - rightMargin, 2);
    		this.onePageFitHeight = toRoundHalfUpFloat(pageHeight - topMargin - bottomMargin, 2);
    		this.pageMaxFitHeight = pageFitHeight;
    		this.pageWidthNum = 1;
    		this.pageHeightNum = 1;
    		this.pageHeightSplitNum = 1;
    		this.canvas = writer.getDirectContent();
    		this.canvases = beginWritingRows(canvas);
    		
    		this.canvasMapping = new HashMap<Integer, PdfPageInfo>();
    		this.canvasMapping.put(Integer.valueOf(pageHeightNum), this);
    	}
    	
    	/**
    	 * 新建一页PDF
    	 * @return
    	 */
    	public PdfPageInfo newPage() {
    		this.pageFitHeight += this.pageHeight;
    		this.pageMaxFitHeight = this.pageFitHeight;
    		++pageHeightNum;
    		++pageHeightSplitNum;
    		document.newPage();
    		writer.setPageEmpty(false); // 将实际白页添加到PDF中(iText ignores document.newPage() if the current page is empty)
    		
    		PdfPageInfo pageInfo = createPdfPageInfo();
    		canvasMapping.put(Integer.valueOf(pageInfo.pageHeightNum), pageInfo);
    		
    		return pageInfo;
    	}
    	
    	private PdfPageInfo createPdfPageInfo() {
    		PdfPageInfo pageInfo = new PdfPageInfo();
    		
    		pageInfo.document = this.document;
    		pageInfo.writer = this.writer;
    		pageInfo.canvas = writer.getDirectContent();
    		pageInfo.canvases = beginWritingRows(pageInfo.canvas);
    		pageInfo.pageSize = document.getPageSize();
    		pageInfo.topMargin = document.topMargin();
    		pageInfo.leftMargin = document.leftMargin();
    		pageInfo.bottomMargin = document.bottomMargin();
    		pageInfo.rightMargin = document.rightMargin();
    		pageInfo.pageWidth = pageSize.getWidth();
    		pageInfo.pageHeight = pageSize.getHeight();
    		pageInfo.pageFitWidth = toRoundHalfUpFloat(pageWidth - rightMargin, 2);
    		pageInfo.pageFitHeight = this.pageFitHeight;
    		pageInfo.onePageFitWidth = toRoundHalfUpFloat(pageWidth - leftMargin - rightMargin, 2);
    		pageInfo.onePageFitHeight = toRoundHalfUpFloat(pageHeight - topMargin - bottomMargin, 2);
    		pageInfo.pageMaxFitHeight = this.pageMaxFitHeight;
    		pageInfo.pageWidthNum = this.pageWidthNum;
    		pageInfo.pageHeightNum = pageHeightNum;
    		pageInfo.pageHeightSplitNum = pageHeightSplitNum;
    		
    		return pageInfo;
    	}
    	
    	/**
    	 * 为水平计算新建一页
    	 * @return
    	 * @see #newPage
    	 */
    	public PdfPageInfo newPageForHorizontal(Integer pageNum) {
    		document.newPage();
    		writer.setPageEmpty(false);
    		
    		PdfPageInfo pageInfo = createPdfPageInfo();
    		pageInfo.pageWidthNum = this.pageWidthNum + 1;
			pageInfo.canvasMapping = new HashMap<Integer, PdfPageInfo>();
			pageInfo.canvasMapping.put(pageNum, pageInfo);
    		
    		return pageInfo;
    	}
    	
    	/**
    	 * 只增加页面参数, 不实际新建一页
    	 * @return
    	 */
    	public PdfPageInfo addNewPage() {
    		this.pageFitHeight += this.pageHeight;
    		this.pageMaxFitHeight = this.pageFitHeight;
    		++pageHeightNum;
    		++pageHeightSplitNum;
    		
    		PdfPageInfo pageInfo = createPdfPageInfo();
    		
    		canvasMapping.put(Integer.valueOf(pageInfo.pageHeightNum), pageInfo);
    		
    		return pageInfo;
    	}
    	
    	/**
    	 * 获得实际要绘制的X坐标
    	 * @param pageNum 页码
    	 * @param xPos 绘制的X坐标
    	 * @return
    	 */
		public float transferToPageXPos(Integer pageNum, float xPos) {
			PdfPageInfo pageInfo = canvasMapping.get(pageNum);
			if (pageInfo == null) {
				return -1;
			}
			
			// 使用整数计算避免浮点计算误差
			int currentMaxWith = (int) (pageInfo.onePageFitWidth * pageInfo.pageWidthNum + 0.1f);
			int intXPos = (int) (xPos + 0.1f);
			if (intXPos > currentMaxWith) {
				return -1;
			} else if (currentMaxWith - intXPos > (int)(pageInfo.onePageFitWidth + 1)) { // 493 - 0 < 492.95
				return -2;
			}
			
			float result = pageInfo.leftMargin;
			if (intXPos == currentMaxWith) {
				result += pageInfo.onePageFitWidth;
			} else if (currentMaxWith - intXPos < (int)pageInfo.onePageFitWidth) {
				float mod = remainderToFloat(xPos + 0.1f, pageInfo.onePageFitWidth);
				if (mod < 1) {
					mod = 0;
    			} else {
    				mod -= 0.1f;
    			}
				result += mod;
			}
			
			return toRoundHalfUpFloat(result, 2);  // 避免490.8999 < 490.90
		}
    	
		/**
		 * 获得实际要绘制的Y坐标
		 * @param pageNum 页码
		 * @param yPos 绘制的Y坐标
		 * @return
		 */
    	public float transferToPageYPos(Integer pageNum, float yPos) {
			PdfPageInfo pageInfo = canvasMapping.get(pageNum);
			if (pageInfo == null) {
				return -1;
			}
			
			// 使用整数计算避免浮点计算误差
			int currentMaxHeight = (int) (pageInfo.onePageFitHeight * pageInfo.pageHeightNum + 0.1);
			int intYPos = (int) (yPos + 0.1);
			if (intYPos > currentMaxHeight) {
			    return -1;
		    } else if (currentMaxHeight - intYPos > (int)(pageInfo.onePageFitHeight + 1)) {
			    return -2;
		    }
			
			float result = pageInfo.bottomMargin;
    		if (intYPos == currentMaxHeight) {

    		} else if (currentMaxHeight - intYPos >= (int)pageInfo.onePageFitHeight) {
    			result += pageInfo.onePageFitHeight;
    		} else {
    			float mod = remainderToFloat(yPos + 0.1f, pageInfo.onePageFitHeight);
    			if (mod < 1) {
    				mod = 0;
    			} else {
    				mod -= 0.1f;
    			}
    			result += (pageInfo.onePageFitHeight - mod);
    		}
    		
    		return toRoundHalfUpFloat(result, 2);  // 避免490.8999 < 490.90
    	}
    	
    	/**
    	 * 获得Y坐标对应的实际页面底部的适合距离
    	 * @param yPos
    	 * @return
    	 */
    	public float transferToFitYPos(float yPos) {
    		float yPosFloat = yPos + 0.1f;
    		int yPosInt = (int) yPosFloat;
    		int onePageFitHeightInt = (int) (onePageFitHeight + 0.1f);
    		if (yPosInt == onePageFitHeightInt) {
    			return toRoundHalfUpFloat(topMargin + onePageFitHeight, 2);
    		}
    		if (yPosInt < onePageFitHeightInt) {
    			return toRoundHalfUpFloat(topMargin + yPos, 2);
    		}
    		
    		int n = (int) ((yPosFloat) / onePageFitHeight);
    		float m = remainderToFloat(yPosFloat, onePageFitHeight); // 避免1460.4 % 486.80002 = 486.79996
    		float result = pageHeight * n - bottomMargin;
    		if (m - 0.1f >= 1) {
    			result += (bottomMargin + topMargin + m - 0.1f);
    		}
    		return toRoundHalfUpFloat(result, 2);  // 避免490.8999 < 490.90
    	}
    	
    	public PdfContentByte getDirectContent() {
    		return writer.getDirectContent();
    	}
    	
        /**
         * Gets and initializes the 4 layers where the table is written to. The text or graphics are added to
         * one of the 4 <CODE>PdfContentByte</CODE> returned with the following order:<p>
         * <ul>
         * <li><CODE>PdfPtable.BASECANVAS</CODE> - the original <CODE>PdfContentByte</CODE>. Anything placed here
         * will be under the table.
         * <li><CODE>PdfPtable.BACKGROUNDCANVAS</CODE> - the layer where the background goes to.
         * <li><CODE>PdfPtable.LINECANVAS</CODE> - the layer where the lines go to.
         * <li><CODE>PdfPtable.TEXTCANVAS</CODE> - the layer where the text go to. Anything placed here
         * will be over the table.
         * </ul><p>
         * The layers are placed in sequence on top of each other.
         * 
         * @param canvas the <CODE>PdfContentByte</CODE> where the rows will
         * be written to
         * @return an array of 4 <CODE>PdfContentByte</CODE>
         * @see #writeSelectedRows(int, int, float, float, PdfContentByte[])
         */    
        public PdfContentByte[] beginWritingRows(PdfContentByte canvas) {
            return new PdfContentByte[]{
                canvas,
                canvas.getDuplicate(),
                canvas.getDuplicate(),
                canvas.getDuplicate(),
            };
        }

        /**
         * Finishes writing the table.
         * 
         * @param canvases the array returned by <CODE>beginWritingRows()</CODE>
         */    
        public void endWritingRows(PdfContentByte[] canvases) {
            PdfContentByte canvas = canvases[PdfPTable.BASECANVAS];
            canvas.saveState();
            canvas.add(canvases[PdfPTable.BACKGROUNDCANVAS]);
            canvas.restoreState();
            canvas.saveState();
            canvas.setLineCap(2);
            canvas.resetRGBColorStroke();
            canvas.add(canvases[PdfPTable.LINECANVAS]);
            canvas.restoreState();
            canvas.add(canvases[PdfPTable.TEXTCANVAS]);
        }
        
        /**
         * 通过Y坐标获得对应页面的画布信息
         * @param yPos
         * @return
         */
        public PdfContentByte[] getWritingRows(float yPos) {
        	Integer pageNum = getPageNum(yPos);
        	PdfPageInfo pageInfo = canvasMapping.get(pageNum);
        	if (pageInfo == null) {
        		return null;
        	}
        	return pageInfo.canvases;
        }
        
        /**
         * 通过页码获得对应页面的画布信息
         * @param pageNum
         * @return
         */
        public PdfContentByte[] getWritingRows(Integer pageNum) {
        	PdfPageInfo pageInfo = canvasMapping.get(pageNum);
        	if (pageInfo == null) {
        		return null;
        	}
        	return pageInfo.canvases;
        }
        
        /**
         * 获得最后一页的画布信息
         * @return
         */
        public PdfContentByte[] getLastWritingRows() {
        	return getWritingRows(pageHeightNum);
        }
        
        /**
         * 获得一个新画布
         * @return
         */
        public PdfContentByte[] getNewWritingRows() {
        	return beginWritingRows(canvas);
        }
        
        /**
         * 通过Y坐标获得存在的页码
         * @param yPos
         * @return
         */
        public Integer getPageNum(float yPos) {
        	int pageNum = ((int) toRoundHalfUpFloat(yPos / onePageFitHeight, 1)) + 1;
        	Integer result = Integer.valueOf(pageNum);
    		if (!canvasMapping.containsKey(result)) {
    			Integer prePageNum = Integer.valueOf(pageNum - 1);
    			return canvasMapping.containsKey(prePageNum) ? prePageNum : result;
    		}
        	
        	return result;
        }
        
        /**
         * 通过页码获得对应的PDF页面信息
         * @param pageNum
         * @return
         */
        public PdfPageInfo getPageInfo(Integer pageNum) {
        	return canvasMapping.get(pageNum);
        }

	}

	/**
	 * <code>PdfTextInfo</code>是文本内容绘制信息
	 */
	static class PdfTextInfo {
		float leading;
		float fontSize;
		float width;
		float height;
		float lineWidth;
		float lineHeight;
		boolean isEnterEnd;
	}
	
	/**
	 * <code>Excel</code>的关键信息
	 */
	static class ExcelCellInfo {
		Report report;
		Sheet sheet;
		List<PdfTableCell> cellImageList;
		List<ChartResultCell> excelCellImageList;
		List<PdfTableCell> floatingList;
		List<FloatingResultCell> excelFloatingList;
	}

	//==================================================
	// PDF Border Style/Event Classes
	//==================================================
	/**
	 * <code>BorderRoundRectangle</code>边框绘制的事件
	 */
	static class BorderRoundRectangle implements PdfPCellEvent {
		
		protected PdfBorderStyle topBorder;
		protected PdfBorderStyle leftBorder;
		protected PdfBorderStyle bottomBorder;
		protected PdfBorderStyle rightBorder;
		protected Object other;

		public void cellLayout(PdfPCell cell, Rectangle position, PdfContentByte[] canvases) {
			PdfContentByte canvas = canvases[PdfContentByte.LINE_JOIN_BEVEL];
			
			DrawRectangle rect = (DrawRectangle) position;

			// Excel是上下边框短, 双线间内容是透明会被背景色填充. 按上下左右绘制, PDF绘制边框是从坐标点向外画
			float xStart = position.getLeft();
			float yStart = position.getTop();
			float xEnd = position.getRight();
			float yEnd = position.getBottom();
			if (topBorder != null && (!topBorder.isDisabled || rect.drawBorderForceTop)) {
				float xStart0 = xStart;
				float xEnd0 = xEnd;
				if (leftBorder != null && (!leftBorder.isDisabled || rect.drawBorderForceLeft)) {
					xStart0 += leftBorder.width;
				}
				if (rightBorder != null && (!rightBorder.isDisabled || rect.drawBorderForceRight)) {
					xEnd0 -= rightBorder.width;
				}
				topBorder.render(canvas, position, xStart0, yStart, xEnd0, yStart,
								 Rectangle.TOP, other == null ? this : other);
			}

			if (bottomBorder != null && (!bottomBorder.isDisabled || rect.drawBorderForceBottom)) {
				float xStart0 = xStart;
				float xEnd0 = xEnd;
				if (leftBorder != null && (!leftBorder.isDisabled || rect.drawBorderForceLeft)) {
					xStart0 += leftBorder.width;
				}
				if (rightBorder != null && (!rightBorder.isDisabled || rect.drawBorderForceRight)) {
					xEnd0 -= rightBorder.width;
				}
				bottomBorder.render(canvas, position, xStart0, yEnd, xEnd0, yEnd, 
				                    Rectangle.BOTTOM, other == null ? this : other);
			}
			
			if (leftBorder != null && (!leftBorder.isDisabled || rect.drawBorderForceLeft)) {
				leftBorder.render(canvas, position, xStart, yStart, xStart, yEnd,
								  Rectangle.LEFT, other == null ? this : other);
			}

			if (rightBorder != null && (!rightBorder.isDisabled || rect.drawBorderForceRight)) {
				rightBorder.render(canvas, position, xEnd, yStart, xEnd, yEnd, 
				                   Rectangle.RIGHT, other == null ? this : other);
			}
		}
		
		/**
		 * @return the topBorder
		 */
		public PdfBorderStyle getTopBorder() {
		
			return topBorder;
		}
		
		/**
		 * @param topBorder the topBorder to set
		 */
		public void setTopBorder(PdfBorderStyle topBorder) {
		
			this.topBorder = topBorder;
		}
		
		/**
		 * @return the leftBorder
		 */
		public PdfBorderStyle getLeftBorder() {
		
			return leftBorder;
		}
		
		/**
		 * @param leftBorder the leftBorder to set
		 */
		public void setLeftBorder(PdfBorderStyle leftBorder) {
		
			this.leftBorder = leftBorder;
		}
		
		/**
		 * @return the bottomBorder
		 */
		public PdfBorderStyle getBottomBorder() {
		
			return bottomBorder;
		}
		
		/**
		 * @param bottomBorder the bottomBorder to set
		 */
		public void setBottomBorder(PdfBorderStyle bottomBorder) {
		
			this.bottomBorder = bottomBorder;
		}
		
		/**
		 * @return the rightBorder
		 */
		public PdfBorderStyle getRightBorder() {
		
			return rightBorder;
		}
		
		/**
		 * @param rightBorder the rightBorder to set
		 */
		public void setRightBorder(PdfBorderStyle rightBorder) {
		
			this.rightBorder = rightBorder;
		}
		
		/**
		 * @return the other
		 */
		public Object getOther() {
		
			return other;
		}
		
		/**
		 * @param other the other to set
		 */
		public void setOther(Object other) {
		
			this.other = other;
		}
		
		public static BorderRoundRectangle copyBorder(BorderRoundRectangle borderRoundRectangle) {
			if (borderRoundRectangle == null) {
				return null;
			}
			BorderRoundRectangle newBorderRoundRectangle = new BorderRoundRectangle();
			newBorderRoundRectangle.topBorder = copyBorder(borderRoundRectangle.topBorder);
			newBorderRoundRectangle.leftBorder = copyBorder(borderRoundRectangle.leftBorder);
			newBorderRoundRectangle.bottomBorder = copyBorder(borderRoundRectangle.bottomBorder);
			newBorderRoundRectangle.rightBorder = copyBorder(borderRoundRectangle.rightBorder);
			return newBorderRoundRectangle;
		}

		public static PdfBorderStyle copyBorder(PdfBorderStyle borderStyle) {
			return borderStyle == null ? null : borderStyle.copyBorderStyle();
		}
	}
	
	static abstract class PdfBorderStyle {
		/**
		 * 边框宽度
		 */
		protected float width;
		/**
		 * 边框颜色
		 */
		protected java.awt.Color color;
		/**
		 * 禁止绘制
		 */
		protected boolean isDisabled;
		
		public PdfBorderStyle(float width, java.awt.Color color) {
			this.width = (width < 0 ? 0 : width);
			this.color = (color != null ? color : java.awt.Color.BLACK);
		}
		
		/**
		 * @return the width
		 */
		public float getWidth() {
		
			return width;
		}

		/**
		 * @return the color
		 */
		public java.awt.Color getColor() {
		
			return color;
		}

		public float getBorderWidth() {
			return width;
		}
		
		public abstract PdfBorderStyle copyBorderStyle();

		/**
		 * 渲染边框
		 * @param canvas com.lowagie.text.pdf.PdfContentByte canvases[PdfPTable.LINECANVAS]
		 * @param position
		 * @param startX
		 * @param startY
		 * @param endX
		 * @param endY
		 */
		public abstract void render(PdfContentByte canvas, Rectangle position, float startX, float startY, float endX, float endY, int side, Object o);

	}
	
	// NONE - HTML:none, borderWith:NONE(1)
	
	// Line - HTML:solid, borderWidth:THIN(1),MEDIUM(2),THICK(3)
	static class LineBorderStyle extends PdfBorderStyle {

		public LineBorderStyle(float width, java.awt.Color color) {
			super(width, color);
		}
		
		public PdfBorderStyle copyBorderStyle() {
			PdfBorderStyle result =  new LineBorderStyle(width, color);
			result.isDisabled = this.isDisabled;
			return result;
		}

		public void render(PdfContentByte canvas, Rectangle position, float startX, float startY, float endX, float endY, int side, Object o) {
			canvas.saveState();
			canvas.setLineWidth(width);
			canvas.setColorStroke(new BaseColor(color.getRGB()));
            canvas.moveTo(startX, startY);
            canvas.lineTo(endX, endY);
            canvas.stroke();
            canvas.restoreState();
		}
		
	}
	
	// Double - HTML:double, borderWidth:DOUBLE(1)
	static class DoubleBorderStyle extends PdfBorderStyle {
		
		public static final float OFFSET = 1.5f;//2f;
		
		protected boolean drawOutTopOrLeft = true;
		protected boolean drawOutBottomOrRight = true;
		protected boolean drawInnerTopOrLeft = false;
		protected boolean drawInnerBottomOrRight = false;

		public DoubleBorderStyle(float width, java.awt.Color color) {
			super(width, color);
		}
		
		public PdfBorderStyle copyBorderStyle() {
			DoubleBorderStyle result =  new DoubleBorderStyle(width, color);
			result.isDisabled = this.isDisabled;
			result.drawOutTopOrLeft = this.drawOutTopOrLeft;
			result.drawOutBottomOrRight = this.drawOutBottomOrRight;
			result.drawInnerTopOrLeft = this.drawInnerTopOrLeft;
			result.drawInnerBottomOrRight = this.drawInnerBottomOrRight;
			return result;
		}

		public void render(PdfContentByte canvas, Rectangle position, float startX, float startY, float endX, float endY, int side, Object o) {
			BorderRoundRectangle borderRoundRectangle = null;
			if (o instanceof BorderRoundRectangle) {
				borderRoundRectangle = (BorderRoundRectangle) o;
			}

			float offset = OFFSET;
			float posOffset = 0.75f;
			float newStartX = startX;
			float newStartY = startY;
			float newEndX = endX;
			float newEndY = endY;
			switch (side) {
				case 1: {
					// top
					newStartY -= offset;
					newEndY -= offset;
					
					if (borderRoundRectangle != null) {
	    				if (borderRoundRectangle.getLeftBorder() != null && borderRoundRectangle.getLeftBorder() instanceof DoubleBorderStyle) {
	    					newStartX += (borderRoundRectangle.getLeftBorder().getBorderWidth() - posOffset);
	    				}
	    				if (borderRoundRectangle.getRightBorder() != null && borderRoundRectangle.getRightBorder() instanceof DoubleBorderStyle) {
	    					newEndX -= (borderRoundRectangle.getRightBorder().getBorderWidth() - posOffset);
	    				}
					}
					
					break;
				}
				case 8: {
					// right
					newStartX -= offset;
					newEndX -= offset;
					
					if (borderRoundRectangle != null) {
	    				if (borderRoundRectangle.getTopBorder() != null && borderRoundRectangle.getTopBorder() instanceof DoubleBorderStyle) {
	    					newStartY -= (borderRoundRectangle.getTopBorder().getBorderWidth() - posOffset);
	    				}
	    				if (borderRoundRectangle.getBottomBorder() != null && borderRoundRectangle.getBottomBorder()instanceof DoubleBorderStyle) {
	    					newEndY += (borderRoundRectangle.getBottomBorder().getBorderWidth() - posOffset);
	    				}
					}
					
					break;
				}
				case 2: {
					// bottom
					newStartY += offset;
					newEndY += offset;
					
					if (borderRoundRectangle != null) {
	    				if (borderRoundRectangle.getLeftBorder() != null && borderRoundRectangle.getLeftBorder() instanceof DoubleBorderStyle) {
	    					newStartX += (borderRoundRectangle.getLeftBorder().getBorderWidth() - posOffset);
	    				}
	    				if (borderRoundRectangle.getRightBorder() != null && borderRoundRectangle.getRightBorder() instanceof DoubleBorderStyle) {
	    					newEndX -= (borderRoundRectangle.getRightBorder().getBorderWidth() - posOffset);
	    				}
					}
					
					break;
				}
				case 4: {
					// left
					newStartX += offset;
					newEndX += offset;
					
					if (borderRoundRectangle != null) {
	    				if (borderRoundRectangle.getTopBorder() != null && borderRoundRectangle.getTopBorder() instanceof DoubleBorderStyle) {
	    					newStartY -= (borderRoundRectangle.getTopBorder().getBorderWidth() - posOffset);
	    				}
	    				if (borderRoundRectangle.getBottomBorder() != null && borderRoundRectangle.getBottomBorder()instanceof DoubleBorderStyle) {
	    					newEndY += (borderRoundRectangle.getBottomBorder().getBorderWidth() - posOffset);
	    				}
					}
					
					break;
				}
				default:
					return;
			}

			canvas.saveState();
			canvas.setLineWidth(width);
			canvas.setColorStroke(new BaseColor(color.getRGB()));

			if (drawOutTopOrLeft && drawOutBottomOrRight) {
				canvas.moveTo(startX, startY);
				canvas.lineTo(endX, endY);
			} else {
				float x = this.getBorderWidth() - posOffset;
				float width = this.width;
				float startXOffset = 0;
				float startYOffset = 0;
				float endXOffset = 0;
				float endYOffset = 0;
				
				if (!drawOutTopOrLeft) {
					startXOffset = x;
					startYOffset = x;
				}
				if (!drawOutBottomOrRight) {
					endXOffset = x;
					endYOffset = x;
				}
				
				switch (side) {
					case 1 :
					case 2 : {
						// top/bottom
						canvas.moveTo(startX + startXOffset - width, startY);
						canvas.lineTo(endX - endXOffset/* + width*/, endY);
						break;
					}
					case 4 :
					case 8 : {
						// left/right
						canvas.moveTo(startX, startY - startYOffset);
						canvas.lineTo(endX, endY + endYOffset);
						break;
					}
					default : {
						break;
					}
				}
			}
			
			if (!drawInnerTopOrLeft && !drawInnerBottomOrRight) {
				canvas.moveTo(newStartX, newStartY);
				canvas.lineTo(newEndX, newEndY);
			} else {
				float x = this.getBorderWidth() - posOffset;
				float width = this.width;
				float startXOffset = 0;
				float startYOffset = 0;
				float endXOffset = 0;
				float endYOffset = 0;
				
				if (drawInnerTopOrLeft) {
					startXOffset = x;
					startYOffset = x;
				}
				if (drawInnerBottomOrRight) {
					endXOffset = x;
					endYOffset = x;
				}
				
				switch (side) {
					case 1 :
					case 2 : {
						// top/bottom
						canvas.moveTo(newStartX - startXOffset - width, newStartY);
						canvas.lineTo(newEndX + endXOffset/* + width*/, newEndY);
						break;
					}
					case 4 :
					case 8 : {
						// left/right
						canvas.moveTo(newStartX, newStartY + startYOffset);
						canvas.lineTo(newEndX, newEndY - endYOffset);
						break;
					}
					default : {
						break;
					}
				}
			}
			
			canvas.stroke();
			canvas.restoreState();
		}
		
		@Override
		public float getBorderWidth() {
			return width * 2 + OFFSET;
		}

	}
	
	// Dashed - HTML:dashed, borderWidth:DASHED(1),MEDIUM_DASHED(2)
	// Not contains DASH_DOT(1),MEDIUM_DASH_DOT(2),DASH_DOT_DOT(1),MEDIUM_DASH_DOT_DOTC(2),SLANTED_DASH_DOT(2)
	static class DashedBorderStyle extends PdfBorderStyle {

		public DashedBorderStyle(float width, java.awt.Color color) {
			super(width, color);
		}
		
		public PdfBorderStyle copyBorderStyle() {
			PdfBorderStyle result =  new DashedBorderStyle(width, color);
			result.isDisabled = this.isDisabled;
			return result;
		}

		public void render(PdfContentByte canvas, Rectangle position, float startX, float startY, float endX, float endY, int side, Object o) {
			canvas.saveState();
			canvas.setLineWidth(width);
			canvas.setColorStroke(new BaseColor(color.getRGB()));
			canvas.setLineDash(3f, 3f);
            canvas.moveTo(startX, startY);
            canvas.lineTo(endX, endY);
            canvas.stroke();
            canvas.restoreState();
		}

	}
	
	// Dotted - HTML:dotted, borderWidth:DOTTED(1),HAIR(1)
	static class DottedBorderStyle extends PdfBorderStyle {

		public DottedBorderStyle(float width, java.awt.Color color) {
			super(width, color);
		}
		
		public PdfBorderStyle copyBorderStyle() {
			PdfBorderStyle result =  new DottedBorderStyle(width, color);
			result.isDisabled = this.isDisabled;
			return result;
		}
		
		public void render(PdfContentByte canvas, Rectangle position, float startX, float startY, float endX, float endY, int side, Object o) {
			canvas.saveState();
			canvas.setLineWidth(width);
			canvas.setColorStroke(new BaseColor(color.getRGB()));
			canvas.setLineDash(0, 4, 2);
            canvas.moveTo(startX, startY);
            canvas.lineTo(endX, endY);
            canvas.stroke();
            canvas.restoreState();
		}

	}
	
	// Dash_Dot - HTML:dashed, borderWidth:DASH_DOT(1),MEDIUM_DASH_DOT(2),DASH_DOT_DOT(1),MEDIUM_DASH_DOT_DOTC(2),SLANTED_DASH_DOT(2)
	static class DashDotBorderStyle extends PdfBorderStyle {

		public DashDotBorderStyle(float width, java.awt.Color color) {
			super(width, color);
		}
		
		public PdfBorderStyle copyBorderStyle() {
			PdfBorderStyle result =  new DashDotBorderStyle(width, color);
			result.isDisabled = this.isDisabled;
			return result;
		}

		public void render(PdfContentByte canvas, Rectangle position, float startX, float startY, float endX, float endY, int side, Object o) {
			canvas.saveState();
			canvas.setLineWidth(width);
			canvas.setColorStroke(new BaseColor(color.getRGB()));
			
			int dotWidth = 1;
			int dashWidth = 4;
			int dotWidthSpace = 5;
			int dashWidthSpace = 8;
			
			if ( ((int)startX) == ((int)endX) ) {
				// left/right
				float top = startY > endY ? endY : startY;
				float bottom = startY > endY ? startY : endY;
				final float X = startX;
				int height = (int) position.getHeight();
	            for (int i = 0, j = 0; j < height; i++) {
	            	if (i % 2 == 0) {
	            		canvas.moveTo(X, bottom);
	            		float y = bottom - dotWidth;
	            		canvas.lineTo(X, y < top ? top : y);
	            		canvas.stroke();
	            		
	            		if (y < top) {
	            			break;
	            		}
	            		bottom -= dotWidthSpace;
	            		j += dotWidthSpace;
	            	} else {
	            		canvas.moveTo(X, bottom);
	            		float y = bottom - dashWidth;
	            		canvas.lineTo(X, y < top ? top : y);
	            		canvas.stroke();
	            		
	            		if (y < top) {
	            			break;
	            		}
	            		bottom -= dashWidthSpace;
	            		j += dashWidthSpace;
	            	}
	            }
				
			} else if ( ((int)startY) == ((int)endY) ){
				// top/bottom
				float left = startX > endX ? endX : startX;
	            float right = startX > endX ? startX : endX;
	            final float Y = endY;
	            int width = (int) position.getWidth();
	            for (int i = 0, j = 0; j < width; i++) {
	            	if (i % 2 == 0) {
	            		canvas.moveTo(right, Y);
	            		float x = right - dotWidth;
	            		canvas.lineTo(x < left ? left : x, Y);
	            		canvas.stroke();
	            		
	            		if (x < left) {
	            			break;
	            		}
	            		right -= dotWidthSpace;
	            		j += dotWidthSpace;
	            	} else {
	            		canvas.moveTo(right, Y);
	            		float x = right - dashWidth;
	            		canvas.lineTo(x < left ? left : x, Y);
	            		canvas.stroke();
	            		
	            		if (x < left) {
	            			break;
	            		}
	            		right -= dashWidthSpace;
	            		j += dashWidthSpace;
	            	}
	            }
			}
			
			canvas.restoreState();

		}

	}
	
	//==================================================
	// PDF Floating Classes
	//==================================================
	/**
	 * <code>PdfFloatingCollection</code>浮动对象操作类
	 */
	static class PdfFloatingCollection {

		protected List<PdfFloating> floatingList;
		
		protected int[] maxWH;
		
		public PdfFloatingCollection() {}
		
		public void addFloating(List<PdfTableCell> cellList) {
			if (cellList != null && cellList.size() > 0) {
				if (floatingList == null) {
				    floatingList = new ArrayList<PdfFloating>();
				}
				for (PdfTableCell cell : cellList) {
					PdfFloating floating = new PdfFloating(cell);
					floatingList.add(floating);
				}
			} 
		}
		
		/**
		 * 获得指定范围内的浮动对象的包含部分
		 * @param pageHeightNum
		 * @param pageWidthNum
		 * @param pageWidth
		 * @param pageHeight
		 * @return
		 */
		public List<PdfFloating> getFloating(int pageHeightNum, int pageWidthNum, float pageWidth, float pageHeight) {
			if (floatingList == null 
					|| floatingList.size() == 0
					|| pageHeightNum < 1
					|| pageWidthNum < 1
					|| pageWidth < 0
					|| pageHeight < 0) {
				return null;
			}
			
			float lastStartXPos = toRoundHalfUpFloat((pageWidthNum - 1) * pageWidth, 2);
			float lastStartYPos = toRoundHalfUpFloat((pageHeightNum - 1) * pageHeight, 2);
			float lastEndXPos = toRoundHalfUpFloat(pageWidthNum * pageWidth, 2);
			float lastEndYPos = toRoundHalfUpFloat(pageHeightNum * pageHeight, 2);
			
			List<PdfFloating> result = new ArrayList<PdfFloating>();
			List<PdfFloating> list = new ArrayList<PdfFloating>();
			for (PdfFloating floating : floatingList) {
				
				float startXPos = toRoundHalfUpFloat(floating.startXPos, 2);
				float startYPos = toRoundHalfUpFloat(floating.startYPos, 2);
				float endXPos = toRoundHalfUpFloat(floating.endXPos, 2);
				float endYPos = toRoundHalfUpFloat(floating.endYPos, 2);
				
				if (endXPos <= lastStartXPos 
						|| startXPos >= lastEndXPos
						|| endYPos <= lastStartYPos 
						|| startYPos >= lastEndYPos) {
					// 不包含在当前页
					list.add(floating);
				} else if (startXPos >= lastStartXPos 
						&& endXPos <= lastEndXPos
						&& startYPos >= lastStartYPos
						&& endYPos <= lastEndYPos) {
					// 完全包含在当前页
					result.add(floating);
					//floating.drawCount++;
				} else {
					// 部分包含
					boolean completed = false;
					
					if (startXPos >= lastStartXPos) {
						floating.drawXPos = startXPos;
						// 左边(整个左边/部分左边)
						if (endYPos <= lastEndYPos && startYPos >= lastStartYPos) {
							floating.rect.drawWidth = lastEndXPos - startXPos;
						} else if (startYPos >= lastStartYPos) { // 上半部
							floating.rect.drawWidth = lastEndXPos - startXPos;
							floating.rect.drawHeight = lastEndYPos - startYPos;
						} else { // 下半部
							floating.drawYPos = lastStartYPos;
							floating.rect.drawWidth = lastEndXPos - startXPos;
							
							float height = floating.cell.getMaxHeight() - (lastStartYPos - startYPos);
							if (height > pageHeight) {
								height = pageHeight;
							}
							floating.rect.drawHeight = height;
						}
						
					} else {
						// 右边(整个右边/部分右边)
						if (endXPos <= lastEndXPos) {
							
							if (endYPos <= lastEndYPos && startYPos >= lastStartYPos) { // 整个
								completed = true; // 绘制完毕
								
								floating.drawXPos = lastStartXPos;
								floating.drawYPos = startYPos;
								floating.rect.drawWidth = endXPos - lastStartXPos;
								floating.rect.drawHeight = floating.cell.getMaxHeight();
							} else if (startYPos >= lastStartYPos) { // 上半部
								floating.drawXPos = lastStartXPos;
								floating.drawYPos = startYPos;
								floating.rect.drawWidth = endXPos - lastStartXPos;
								floating.rect.drawHeight = lastEndYPos - startYPos;
							} else { // 下半部
								floating.drawXPos = lastStartXPos;
								floating.drawYPos = lastStartYPos;
								
								floating.rect.drawWidth = endXPos - lastStartXPos;
								
								float height = floating.cell.getMaxHeight() - (lastStartYPos - startYPos);
								if (height > pageHeight) {
									height = pageHeight;
								} else {
									completed = true; // 绘制完毕
								}
								floating.rect.drawHeight = height;
							}
							
						} else {
							// 不是最左和最右边, 此为多列切分中间部分。类似右边处理, 只是不会绘制完毕
							if (endYPos <= lastEndYPos && startYPos >= lastStartYPos) { // 整个中间
								floating.drawXPos = lastStartXPos;
								floating.drawYPos = startYPos;  
								floating.rect.drawWidth = pageWidth;
								floating.rect.drawHeight = floating.cell.getMaxHeight();
							} else if (startYPos >= lastStartYPos) { // 中间上半部
								floating.drawXPos = lastStartXPos;
								floating.drawYPos = startYPos;
								floating.rect.drawWidth = pageWidth;
								floating.rect.drawHeight = lastEndYPos - startYPos;
							} else {  // 中间下半部
								floating.drawXPos = lastStartXPos;
								floating.drawYPos = lastStartYPos;
								
								floating.rect.drawWidth = pageWidth;
								
								float height = floating.cell.getMaxHeight() - (lastStartYPos - startYPos);
								if (height > pageHeight) {
									height = pageHeight;
								}
								floating.rect.drawHeight = height;
							}
						}
					} 
					
					result.add(floating);
					
					// 如果是最后部分, 则不需要再添加到list
					if (!completed) {
						list.add(floating);
					}
					
					//floating.drawCount++;
				}
			}

			if (floatingList.size() != list.size()) {
				floatingList.clear();
				for (int i = 0, j = list.size(); i < j; i++) {
					PdfFloating floating = list.get(i);
					floatingList.add(floating);
				}
			}
			
			return result;
		}
		
		/**
		 * 获得浮动对象最大的宽高
		 * @param pageWidth
		 * @param pageHeight
		 * @return
		 */
		public int[] getFloatingMaxWH(float pageWidth, float pageHeight) {
			int[] result = new int[]{0, 0};
			if (!hasFloating()) {
				return result;
			}
			
			float w = 0;
			float h = 0;
			for (PdfFloating floating : floatingList) {
				if (floating.endXPos > w) {
					w = floating.endXPos;
				}
				if (floating.endYPos > h) {
					h = floating.endYPos;
				}
			}
			
			// 避免浮点计算误差
			w = toRoundHalfUpFloat(w, 2);
			h = toRoundHalfUpFloat(h, 2);
			pageWidth = toRoundHalfUpFloat(pageWidth, 2);
			pageHeight = toRoundHalfUpFloat(pageHeight, 2);

			int n = (int) (w / pageWidth);
			float m = remainderToFloat(w, pageWidth);
			if (m > 0) {
				++n;
			}
			w = n;
			
			n = (int) (h / pageHeight);
			m = remainderToFloat(h, pageHeight);
			if (m > 0) {
				++n;
			}
			h = n;
			
			result[0] = (int) w;
			result[1] = (int) h;
			
			return result;
		}
		
		public boolean hasFloating() {
			return floatingList != null && floatingList.size() > 0;
		}
	}
	
	/**
	 * <code>PdfFloating</code>表示PDF中指定位置的对象(浮动对象)
	 */
	static class PdfFloating {

		PdfTableCell cell;
		float startXPos;
		float startYPos;
		float endXPos;
		float endYPos;
		float drawXPos;
		float drawYPos;
		DrawRectangle rect;
		//int drawCount;
		
		public PdfFloating(PdfTableCell cell) {
			this.cell = cell;
			float w = cell.maxWidth;
			float h = cell.getMaxHeight();
			float llx = cell.xPos;
			float lly = cell.yPos - h;
			float urx = cell.xPos + w;
			float ury = cell.yPos;
			
			startXPos = cell.xPos;
			startYPos = cell.yPos;
			endXPos = cell.xPos + w;
			endYPos = cell.yPos + h;
			
			this.drawXPos = startXPos;
			this.drawYPos = startYPos;
			
			this.rect = new DrawRectangle(llx, lly, urx, ury, 
			                              cell.xPos, cell.yPos, w, h);
		}
	}
	
	//==================================================
	// PDF Other Classes
	//==================================================
	/**
	 * <code>DrawRectangle</code>绘制区域
	 */
	static class DrawRectangle extends Rectangle {
		
		protected float drawXPos;
		protected float drawYPos;
		protected float drawWidth;
		protected float drawHeight;
		protected float drawBorderWidthLeft;
		protected float drawBorderWidthRight;
		protected float drawBorderWidthTop;
		protected float drawBorderWidthBottom;
		protected boolean drawBorderForceLeft;
		protected boolean drawBorderForceRight;
		protected boolean drawBorderForceTop;
		protected boolean drawBorderForceBottom;

		public DrawRectangle(float llx, float lly, float urx, float ury) {
			
			super(llx, lly, urx, ury);
		}

		public DrawRectangle(float llx, float lly, float urx, float ury, float drawXPos, float drawYPos, float drawWidth,
							 float drawHeight) {

			super(llx, lly, urx, ury);
			this.drawXPos = drawXPos;
			this.drawYPos = drawYPos;
			this.drawWidth = drawWidth;
			this.drawHeight = drawHeight;
		}
		
		public static DrawRectangle copyBorder(Rectangle rect) {
			DrawRectangle newRect = new DrawRectangle(rect.getLeft(), rect.getBottom(), rect.getRight(), rect.getTop());
			
			if (!rect.hasBorders()) {
				newRect.setBorder(NO_BORDER);
			} else {
				newRect.setBorderWidthLeft(rect.getBorderWidthLeft());
				newRect.setBorderWidthRight(rect.getBorderWidthRight());
				newRect.setBorderWidthTop(rect.getBorderWidthTop());
				newRect.setBorderWidthBottom(rect.getBorderWidthBottom());
			}
			
			if (rect instanceof DrawRectangle) {
				DrawRectangle drawRect =(DrawRectangle) rect;
				newRect.drawXPos = drawRect.drawXPos;
				newRect.drawYPos = drawRect.drawYPos;
				newRect.drawWidth = drawRect.drawWidth;
				newRect.drawHeight = drawRect.drawHeight;
				newRect.drawBorderWidthLeft = drawRect.drawBorderWidthLeft;
				newRect.drawBorderWidthRight = drawRect.drawBorderWidthRight;
				newRect.drawBorderWidthTop = drawRect.drawBorderWidthTop;
				newRect.drawBorderWidthBottom = drawRect.drawBorderWidthBottom;
				
				newRect.drawBorderForceLeft = drawRect.drawBorderForceLeft;
				newRect.drawBorderForceRight = drawRect.drawBorderForceRight;
				newRect.drawBorderForceTop = drawRect.drawBorderForceTop;
				newRect.drawBorderForceBottom = drawRect.drawBorderForceBottom;
			}
			
			return newRect;
		}
		
		public boolean hasDrawBorder() {
			return border > 0;
		}

		/**
		 * @return the drawXPos
		 */
		public float getDrawXPos() {
		
			return drawXPos;
		}
		
		/**
		 * @param drawXPos the drawXPos to set
		 */
		public void setDrawXPos(float drawXPos) {
		
			this.drawXPos = drawXPos;
		}
		
		/**
		 * @return the drawYPos
		 */
		public float getDrawYPos() {
		
			return drawYPos;
		}
		
		/**
		 * @param drawYPos the drawYPos to set
		 */
		public void setDrawYPos(float drawYPos) {
		
			this.drawYPos = drawYPos;
		}
		
		/**
		 * @return the drawWidth
		 */
		public float getDrawWidth() {
		
			return drawWidth;
		}
		
		/**
		 * @param drawWidth the drawWidth to set
		 */
		public void setDrawWidth(float drawWidth) {
		
			this.drawWidth = drawWidth;
		}
	
		/**
		 * @return the drawHeight
		 */
		public float getDrawHeight() {
		
			return drawHeight;
		}

		/**
		 * @param drawHeight the drawHeight to set
		 */
		public void setDrawHeight(float drawHeight) {
		
			this.drawHeight = drawHeight;
		}
		
		/**
		 * @return the drawBorderWidthLeft
		 */
		public float getDrawBorderWidthLeft() {
			if (drawBorderWidthLeft > 0) {
				return drawBorderWidthLeft;
			}
			if (this.hasBorder(Rectangle.LEFT)) {
				float result = this.getBorderWidthLeft();
				return result > 0 ? result : 0;
			}
			return 0;
		}
		
		/**
		 * @param drawBorderWidthLeft the drawBorderWidthLeft to set
		 */
		public void setDrawBorderWidthLeft(float drawBorderWidthLeft) {
		
			this.drawBorderWidthLeft = drawBorderWidthLeft;
		}
		
		/**
		 * @return the drawBorderWidthRight
		 */
		public float getDrawBorderWidthRight() {
			if (drawBorderWidthRight > 0) {
				return drawBorderWidthRight;
			}
			if (this.hasBorder(Rectangle.RIGHT)) {
				float result = this.getBorderWidthRight();
				return result > 0 ? result : 0;
			}
			return 0;
		}
		
		/**
		 * @param drawBorderWidthRight the drawBorderWidthRight to set
		 */
		public void setDrawBorderWidthRight(float drawBorderWidthRight) {
		
			this.drawBorderWidthRight = drawBorderWidthRight;
		}
		
		/**
		 * @return the drawBorderWidthTop
		 */
		public float getDrawBorderWidthTop() {
			if (drawBorderWidthTop > 0) {
				return drawBorderWidthTop;
			}
			if (this.hasBorder(Rectangle.TOP)) {
				float result = this.getBorderWidthTop();
				return result > 0 ? result : 0;
			}
			return 0;
		}

		/**
		 * @param drawBorderWidthTop the drawBorderWidthTop to set
		 */
		public void setDrawBorderWidthTop(float drawBorderWidthTop) {
		
			this.drawBorderWidthTop = drawBorderWidthTop;
		}

		/**
		 * @return the drawBorderWidthBottom
		 */
		public float getDrawBorderWidthBottom() {
			if (drawBorderWidthBottom > 0) {
				return drawBorderWidthBottom;
			}
			if (this.hasBorder(Rectangle.BOTTOM)) {
				float result = this.getBorderWidthBottom();
				return result > 0 ? result : 0;
			}
			return 0;
		}

		/**
		 * @param drawBorderWidthBottom the drawBorderWidthBottom to set
		 */
		public void setDrawBorderWidthBottom(float drawBorderWidthBottom) {
		
			this.drawBorderWidthBottom = drawBorderWidthBottom;
		}

	}
	
	/**
	 * <code>PdfImage</code>PDF图片
	 */
	static class PdfImage extends Image {
		
		protected byte[] imgBytes;

		protected PdfImage(Image image) {
			super(image);
		}
		
		public PdfImage(URL url) {
			super(url);
		}
		
		public PdfImage(Image image, byte[] imgBytes) {
			super(image);
			this.imgBytes = imgBytes;
		}
		
		/**
		 * @return the imgBytes
		 */
		public byte[] getImgBytes() {
		
			return imgBytes;
		}
		
		/**
		 * @param imgBytes the imgBytes to set
		 */
		public void setImgBytes(byte[] imgBytes) {
		
			this.imgBytes = imgBytes;
		}

	}
	
	/**
	 * <code>PdfPosition</code>PDF绘制位置
	 */
	static class PdfPosition implements net.coobird.thumbnailator.geometry.Position {
		private int xOffset;
		private int yOffset;
		
		public PdfPosition(int xOffset, int yOffset) {
			this.xOffset = xOffset;
			this.yOffset = yOffset;
		}
		
		public Point calculate(int enclosingWidth, int enclosingHeight,
			       				int width, int height, int insetLeft, int insetRight,
			       				int insetTop, int insetBottom) {
			int x = insetLeft + xOffset;
			int y = insetTop + yOffset;
			return new Point(x, y);
	    }
	}
	
	/**
	 * <code>PdfFontSelector</code>字体选择器
	 */
	static class PdfFontSelector extends FontSelector {

		public void setFontsSize(float fontSize) {
			int size = fonts.size();
			if (size == 0) {
				return;
			}
			for (int i = 0; i < size; ++i) {
				((Font) fonts.get(i)).setSize(fontSize);
			}
		}
		
		public Font getFont(int index) {
			return (Font) fonts.get(index);
		}
	}
	
	//==================================================
	// Draw Strategy Classes
	//==================================================
	interface DrawStrategy {
		
		public float getDrawBodyWidth();
		public void setReportExcelPdfTable(ReportExcelPdfTable reportExcelPdfTable);
		public void setDrawBodyWidth(float drawBodyWidth);
		public float getDrawBodyHeight();
		public void setDrawBodyHeight(float drawBodyHeight);
		
		public void calculate(PdfTableCellInfo info);
	}
	
	static abstract class AbstractDrawStrategy implements DrawStrategy {
		
		protected ReportExcelPdfTable pdfTable;
		protected float drawBodyWidth;
		protected float drawBodyHeight;
		protected PdfTableCellInfo info;
		
		public void calculate(PdfTableCellInfo info) {
			this.info = info;

			calculateCellsPos();
			
			// Date: 2017-06-16 - 浮动图片floating顶点对其行格左上角坐标
			calculateFloatingPos();
			
			calculateCellImageSize();
		}
		
		/**
		 * 计算每个格子的坐标
		 * @param splitHeight
		 */
		protected void calculateCellsPos() {
			calculateYPosAndSave();
			
			calculateCellsXYPos(info.title);
			calculateCellsXYPos(info.header);
			calculateCellsXYPos(info.detail);
			if (drawBodyHeight < 1) {
				calculateCellsXYPos(info.footer);

				addAllToCells(info.title);
				addAllToCells(info.header);
				addAllToCells(info.detail);
				addAllToCells(info.footer);
			} else {
			    calculateCellsPos(drawBodyHeight);
			}
		}
		
		/**
		 * 计算浮动对象左上角坐标. 根据最新的格子每行的坐标计算
		 */
		protected abstract void calculateFloatingPos();
		
		/**
		 * 重新计算格子图片的大小
		 */
		protected abstract void calculateCellImageSize();
		
		/**
		 * 计算每个格子的坐标
		 * @param splitHeight
		 */
		protected abstract void calculateCellsPos(float splitHeight);

		/**
		 * 计算每个格子占用的rowspan
		 * @param list
		 * @return
		 */
		protected int calculateRowspan(List<PdfTableCell> list) {
			if (list == null || list.size() == 0) {
				return 0;
			}
			
			int result = 0;
			for (PdfTableCell cell : list) {
				if (cell.rowIndex > result) {
					result = cell.rowIndex;
				}
			}
			return result + 1;
		}
		
		/**
		 * 计算列表中每个格子的高度
		 * @param list
		 * @return
		 */
		protected float calculateHeight(List<PdfTableCell> list, boolean useRowIndexPosMapping) {
			// 要求计算的格子的heightArray都已计算出来
			// @see #calculateYPosAndSave
			Map<Integer, PdfTableCellPos> rowIndexPosMapping = null;
			if (useRowIndexPosMapping) {
				rowIndexPosMapping = info.rowIndexPosMapping;
			} else {
				rowIndexPosMapping = new LinkedHashMap<Integer, PdfTableCellPos>();
				calculateYPosAndSave(rowIndexPosMapping, list);
			}
			
			float height = 0;
			int preRowIndex = -1;
			PdfTableCellPos prePos = null;
			for (PdfTableCell cell : list) {
				PdfTableCellPos pos = rowIndexPosMapping.get(Integer.valueOf(cell.rowIndex));
				
				if (pos != null) {
					if (cell.rowIndex > preRowIndex) {
						
						if (prePos == null) {
							prePos = pos;
							preRowIndex = cell.rowIndex;
						}
						
						int n = cell.rowIndex - preRowIndex;
						if (n > 1 && prePos.cell.getRowspan() > 1) {
							int maxSize = prePos.heightArray.length;
							n = n > maxSize ? maxSize : n;
							
							for (int i = 1; i < n; ++i) {
								height += prePos.heightArray[i];
							}
						}
						if (pos.cell.getRowspan() > 1) {
							height += pos.heightArray[0]; // 如果最后一个格子是合并多行的, 那0就是格子的总高度
						} else {
							height += pos.height;
						}
						
						preRowIndex = cell.rowIndex;
						prePos = pos;
					}
				}
			}
			
			return height;
		}
		
		/**
		 * 添加格子到要绘制的列表
		 * @param list
		 */
		protected void addAllToCells(List<PdfTableCell> list) {
			if (list != null && list.size() > 0) {
				info.cells.addAll(list);
			}
		}
		
		/**
		 * 计算每个格子的X坐标, 同时将之前#calculateYPosAndSave方法得到的Y坐标赋值给每个格子实例
		 * @param list
		 */
		protected void calculateCellsXYPos(List<PdfTableCell> list) {
			if (list == null || list.size() == 0) {
				return;
			}
			
			Map<Integer, PdfTableCellPos> rowIndexPosMapping = info.rowIndexPosMapping;
			Map<Integer, PdfTableCellPos> colIndexPosMapping = info.colIndexPosMapping;
			
			for (PdfTableCell cell : list) {
				Integer colkey = Integer.valueOf(cell.colIndex);
				PdfTableCellPos colValue = colIndexPosMapping.get(colkey);
				if (colValue != null) {
					cell.xPos = colValue.xPos;
				} else {
					float xPos = info.calculateXPos(cell);
					colValue = new PdfTableCellPos();
					colValue.xPos = xPos;
					colIndexPosMapping.put(colkey, colValue);
					cell.xPos = xPos;
				}
				
				Integer rowkey = Integer.valueOf(cell.rowIndex);
				PdfTableCellPos rowValue = rowIndexPosMapping.get(rowkey);
				cell.yPos = rowValue.yPos;
				
				float width = info.getCellWidth(cell.colIndex, cell.getColspan());
				cell.maxWidth = width;
				
				cell.drawWidth = cell.maxWidth;
				cell.drawHeight = cell.getMaxHeight();
				
				cell.originalXPos = cell.xPos;
				cell.originalYPos = rowValue.yPos;
				cell.originalWidth = cell.maxWidth;
				cell.originalHeight = cell.getMaxHeight();
			}
		}
		
		/**
		 * 计算每行格子的Y坐标
		 */
		protected void calculateYPosAndSave() {
			Map<Integer, PdfTableCellPos> map = new LinkedHashMap<Integer, PdfTableCellPos>();
			
			calculateYPosAndSave(map, info.title);
			calculateYPosAndSave(map, info.header);
			calculateYPosAndSave(map, info.detail);
			calculateYPosAndSave(map, info.footer);
			
			float yPos = 0;
			for (Iterator<PdfTableCellPos> iter = map.values().iterator(); iter.hasNext(); ) {
				PdfTableCellPos pos = iter.next();
				
				if (pos.cell.rowIndex > 0) {
					PdfTableCellPos prePos = map.get(Integer.valueOf(pos.cell.rowIndex - 1));
					
					if (prePos == null) {
						for (int i = pos.cell.rowIndex - 2; i > -1; i--) {
							prePos = map.get(Integer.valueOf(i));
							if (prePos != null) {
								break;
							}
						}
						if (prePos == null) {
							throw new RuntimeException("cell information error");
						}
					}
					
					if (prePos.cell.getRowspan() == 1) {
						yPos = prePos.yPos + prePos.height;
					} else {
						yPos = prePos.yPos;
						int subRowIndex = prePos.cell.rowIndex;
						for (int i = 0, currentPosRowIndex = pos.cell.rowIndex; i < prePos.heightArray.length && subRowIndex < currentPosRowIndex; i++, subRowIndex++) {
    						yPos += prePos.heightArray[i];
					    }
					}
					
					pos.yPos = yPos;
				}
				
				PdfTableCellPos posValue = new PdfTableCellPos();
				posValue.cell = pos.cell;
				posValue.height = pos.height;
				posValue.yPos = yPos;
				posValue.heightArray = pos.heightArray;
				info.rowIndexPosMapping.put(Integer.valueOf(pos.cell.rowIndex), posValue);
			}
		}
		
		/**
		 * 计算每行最小高度的格子
		 * @param map
		 * @param list
		 */
		protected void calculateYPosAndSave(Map<Integer, PdfTableCellPos> map, List<PdfTableCell> list) {
			if (list == null || list.size() == 0) {
				return;
			}
			
			Map<Integer, PdfTableCellPos> multiRowspanMap = new HashMap<Integer, PdfTableCellPos>();
			
			int maxRowIndex = 0;
			
			for (PdfTableCell cell : list) {
				float cellHeight = cell.getMaxHeight();
				Integer key = Integer.valueOf(cell.rowIndex);
				PdfTableCellPos pos = map.get(key);
				if (pos == null) {
					pos = new PdfTableCellPos();
					pos.cell = cell;
					pos.height = cellHeight;
					map.put(key, pos);
					
					if (cell.getRowspan() > 1) {
						multiRowspanMap.put(key, pos);
					}
				} else if (pos.height > cellHeight) {
					pos.cell = cell;
					pos.height = cellHeight; // 获得一行最小高度
					
					if (cell.getRowspan() == 1) {
						multiRowspanMap.remove(key);
					}
				}
				
				if (cell.rowIndex > maxRowIndex) {
					maxRowIndex = cell.rowIndex;
				}
			}
			
			// 计算多行为最小行时每行的高度
			if (multiRowspanMap.size() > 0) {
				for (Iterator<PdfTableCellPos> iter = multiRowspanMap.values().iterator(); iter.hasNext(); ) {
					PdfTableCellPos pos = iter.next();
					
					if (pos.heightArray != null) {
						continue;
					}
					
					pos.heightArray = calculateOneRowHeight(map, pos, maxRowIndex);
				}
			}
		}
		
		private float[] calculateOneRowHeight(Map<Integer, PdfTableCellPos> map, PdfTableCellPos pos, int maxRowIndex) {
			if (pos.cell.getRowspan() == 1) {
				return new float[]{ pos.height };
			}
			
			if (pos.heightArray != null) {
				return pos.heightArray;
			}
			
			pos.heightArray = new float[pos.cell.getRowspan()];
			
			float height = 0f;
			for (int i = 1, j = pos.cell.getRowspan(), startRowIndex = pos.cell.rowIndex; i < j; ) {
				PdfTableCellPos nextPos = map.get(Integer.valueOf(startRowIndex + i));
				
				if (nextPos == null) {
					if (startRowIndex + i > maxRowIndex) {
						break; // last cell
					}
					++i;
					continue;
				}
				
				if (nextPos.cell.getRowspan() == 1) {
					pos.heightArray[i] = nextPos.height;
					height += nextPos.height;
					++i;
				} else {
					float[] heightArray = calculateOneRowHeight(map, nextPos, maxRowIndex);
					for (int k = 0, l = i, limit = pos.heightArray.length; k < heightArray.length && l < limit; k++, l++) {
						pos.heightArray[l] = heightArray[k];
						height += heightArray[k];
					}
					i += heightArray.length;
				}
			}
			
			pos.heightArray[0] = pos.height - height;

			return pos.heightArray;
		}
		
		/**
		 * @return the drawBodyWidth
		 */
		public float getDrawBodyWidth() {
		
			return drawBodyWidth;
		}
		
		/**
		 * @param drawBodyWidth the drawBodyWidth to set
		 */
		public void setDrawBodyWidth(float drawBodyWidth) {
		
			this.drawBodyWidth = drawBodyWidth;
		}
		
		/**
		 * @return the drawBodyHeight
		 */
		public float getDrawBodyHeight() {
		
			return drawBodyHeight;
		}
		
		/**
		 * @param drawBodyHeight the drawBodyHeight to set
		 */
		public void setDrawBodyHeight(float drawBodyHeight) {
		
			this.drawBodyHeight = drawBodyHeight;
		}

		
		/**
		 * @return the pdfTable
		 */
		public ReportExcelPdfTable getReportExcelPdfTable() {
		
			return pdfTable;
		}

		
		/**
		 * @param pdfTable the pdfTable to set
		 */
		public void setReportExcelPdfTable(ReportExcelPdfTable pdfTable) {
		
			this.pdfTable = pdfTable;
		}
	}
	
	/**
	 * <code>SpecialRowSplitStrategy</code>特殊的行切策略.<br/>
	 * 此策略对于宽度超过指定宽度的内容直接列切, 对于高度超过指定高度的内容分两种情况对待:<br/>
	 * 1: 如果内容高度超过指定高度, 但是小于等于下一页面高度, 则此内容被绘制到下一页.<br/>
	 * 2: 如果内容高度超过指定高度, 但是大于下一页面高度, 也就是内容跨多页的情况, 则此内容使用直接行切.<br/>
	 * <p/>
	 * 对于使用了Excel"设置单元格格式-对齐-文本控制"的内容会按对应方式重新计算格子大小.
	 */
	static class SpecialRowSplitStrategy extends AbstractDrawStrategy {
		
		private static final String TEXT_CONTROL = "tc";
		
		@Override
		protected void calculateFloatingPos() {
			List<PdfTableCell> floatingList = info.floating;
			if (floatingList == null || floatingList.size() == 0) {
				return;
			}
			
			Map<Integer, PdfTableCellPos> rowIndexPosMapping = info.rowIndexPosMapping;
			Map<Integer, PdfTableCellPos> colIndexPosMapping = info.colIndexPosMapping;
			for (int i = 0, j = floatingList.size(); i < j; ++i) {
				PdfTableCell floating = floatingList.get(i);
				
				PdfTableCellPos colPos = colIndexPosMapping.get(Integer.valueOf(floating.colIndex));
				if (colPos != null) {
					floating.xPos = colPos.xPos;
				}
				
				PdfTableCellPos rowPos = rowIndexPosMapping.get(Integer.valueOf(floating.rowIndex));
				if (rowPos != null) {
					floating.yPos = rowPos.yPos;
				}
			}
		}
		
		@Override
		protected void calculateCellImageSize() {
			ExcelCellInfo excelCellInfo = info.excelCellInfo;
			if (excelCellInfo == null || excelCellInfo.cellImageList == null || excelCellInfo.cellImageList.size() == 0) {
				return;
			}
			
			List<PdfTableCell> cellImageList = excelCellInfo.cellImageList;
			List<ChartResultCell> excelCellImageList = excelCellInfo.excelCellImageList;
			Report report = excelCellInfo.report;

			try {
    			for (int i = 0, j = cellImageList.size(); i < j; ++i) {
    				PdfTableCell cell = cellImageList.get(i);
    				ChartResultCell rc = excelCellImageList.get(i);
    				
    				Image image = cell.getImage();
    				int imageWidth = (int) image.getWidth();
    				int imageHeight = (int) image.getHeight();
    				int widthPx = (int) ((cell.maxWidth - cell.getBorderWidthLeft() - cell.getBorderWidthRight() - 1) / UNIT_PX_TO_PT_FAC);
    				int heightPx = (int) ((cell.getFixedHeight() - cell.getBorderWidthTop() - cell.getBorderWidthBottom() - 1) / UNIT_PX_TO_PT_FAC);
    				
    				if (imageWidth > widthPx + 2 
    						|| imageWidth < widthPx - 2 
    						|| imageHeight > heightPx + 2 
    						|| imageHeight < heightPx - 2) {
        				byte[] imgb = com.yss.sofa.report.engine.util.ChartUtil.generatePNG(rc, 
        			                                                                        widthPx, 
        			                                                                        heightPx,
        								                                                    report.getParams(),
        								                                                    report.getConsts(),
        								                                                    report.getDatas());
        				Image img = Image.getInstance(imgb);
        				PdfImage pdfImage = new PdfImage(img, imgb);
        				cell.setImage(pdfImage);
    				}

    			}
			} catch (Exception e) {
				throw new RuntimeException("格子内图片生成错误 ");
			}
		}
		
		@Override
		protected void calculateCellsPos() {
			// Excel"设置单元格格式-对齐-文本控制"
			Flag flag = recalculateCellsForTextControl();
			if (flag.attr.get(TEXT_CONTROL) != null) {
    			info.rowIndexPosMapping = new HashMap<Integer, PdfTableCellPos>();
    			info.colIndexPosMapping = new HashMap<Integer, PdfTableCellPos>();
    			super.calculateCellsPos();
			} else {
				if (drawBodyHeight < 1) {
					addAllToCells(info.title);
					addAllToCells(info.header);
					addAllToCells(info.detail);
					addAllToCells(info.footer);
				} else {
				    calculateCellsPos(drawBodyHeight);
				}
			}
		}

		/**
		 * 从新计算每行格子的高度
		 * @see #calculateCellsPos(float splitHeight)
		 * @see #addRow
		 */
		private Flag recalculateCellsForTextControl() {
			calculateYPosAndSave();
			
			calculateCellsXYPos(info.title);
			calculateCellsXYPos(info.header);
			calculateCellsXYPos(info.detail);
			calculateCellsXYPos(info.footer);
			
			Flag flag = new Flag();
			flag.attr = new HashMap<String, Object>();
			flag.attr.put(TEXT_CONTROL, null);
			recalculateCellsForTextControl(info.title, flag);
			recalculateCellsForTextControl(info.header, flag);
			recalculateCellsForTextControl(info.detail, flag);
			recalculateCellsForTextControl(info.footer, flag);

			return flag;
		}
		
		private void recalculateCellsForTextControl(List<PdfTableCell> cells, Flag flag) {
			if (cells == null || cells.size() == 0) {
				return;
			}
			
			PdfTableCellPos currentPos = new PdfTableCellPos();
			PdfTableCell firstCell = cells.get(0);
			currentPos.cell = firstCell;
			currentPos.height = firstCell.getMaxHeight();
			currentPos.yPos = firstCell.yPos;

			List<PdfTableCell> row_ = new ArrayList<PdfTableCell>();
			List<List<PdfTableCell>> rows = new ArrayList<List<PdfTableCell>>();
			rows.add(row_);

			for (int cellIndex = 0, cellSize = cells.size(), currentRowIndex = firstCell.rowIndex; cellIndex < cellSize; cellIndex++) {
				PdfTableCell xcell = cells.get(cellIndex);
				if (currentRowIndex == xcell.rowIndex) {
					row_.add(xcell); // 找出按一行高度最大的格子为行高的一行全部格子, 并获得最大高度信息
					if (xcell.getMaxHeight() > currentPos.height) {
						currentPos.cell = xcell;
						currentPos.height = xcell.getMaxHeight();
					}
				} else if (xcell.yPos + xcell.getMaxHeight() < currentPos.yPos + currentPos.height + 1) { // +1避免浮点误差
					row_.add(xcell);
				} else if (xcell.yPos < currentPos.yPos + currentPos.height - 1f) {
					row_.add(xcell);
					currentPos.height += (xcell.yPos + xcell.getMaxHeight() - currentPos.yPos - currentPos.height);
				} else {
					
					processCellsBorder(row_, rows);
					recalculateCellsForTextControl(row_, currentPos, flag);
					flag.lastCell = row_.get(row_.size() - 1);
					
					currentRowIndex = xcell.rowIndex;
					--cellIndex;
					row_ = new ArrayList<PdfTableCell>();
					rows.add(row_);
					
					currentPos = new PdfTableCellPos();
					currentPos.cell = xcell;
					currentPos.height = xcell.getMaxHeight();
					currentPos.yPos = xcell.yPos;
				}
			}
			
			// 处理最后的格子
			if (row_.size() > 0) {
				processCellsBorder(row_, rows);
				recalculateCellsForTextControl(row_, currentPos, flag);
			}
			
			flag.lastCell = cells.get(cells.size() - 1);
			
		}
		
		/**
		 * 处理分页处上一页最后一行表格的底部边框
		 * @param wordCells
		 */
		protected void processCellsBorderForPaging(List<PdfTableCell> wordCells) {
			if (wordCells == null || wordCells.size() == 0) {
				return;
			}
			
			int size = wordCells.size();
			PdfTableCell lastCell = wordCells.get(size - 1);
			
			TreeSet<Integer> colIndexSet = new TreeSet<Integer>();
			int preColIndex = lastCell.colIndex + lastCell.getColspan();
			int idx = size - 1;
			for (int lastRowIndex = lastCell.rowIndex; idx > -1; --idx) {
				PdfTableCell xcell = wordCells.get(idx);
				if (xcell.rowIndex != lastRowIndex) {
					// 如果前面或后面是是合并单元格, 则需要增加到处理集合中
					PdfTableCell preCell = wordCells.get(idx + 1);
					if (preCell.colIndex != 0) {
						// 合并单元格
						for (int start = 0, end = preCell.colIndex; start < end; ++start) {
							colIndexSet.add(Integer.valueOf(start));
						}
					}
					if (lastCell.colIndex + lastCell.getColspan() - 1 < info.colCount) {
						// 合并单元格
						for (int start = lastCell.colIndex + lastCell.getColspan(), end = info.colCount + 1; start < end; ++start) {
							colIndexSet.add(Integer.valueOf(start));
						}
					}
					break;
				}
				
				if (xcell.colIndex + xcell.getColspan() == preColIndex) {
					preColIndex = xcell.colIndex;
				} else {
					// 合并单元格
					for (int start = xcell.colIndex + xcell.getColspan(), end = preColIndex; start < end; ++start) {
						colIndexSet.add(Integer.valueOf(start));
					}
				}
				
				// 处理底边
				if (xcell.bottomBorderWidth > 0) {
					BorderRoundRectangle borderRoundRectangle = (BorderRoundRectangle) xcell.getCellEvent();
					borderRoundRectangle.bottomBorder.isDisabled = false;
				}
			}
			
			if (colIndexSet.size() > 0) {
				int minColIndex = colIndexSet.first().intValue();
				int maxColIndex = colIndexSet.last().intValue();
				
				for (; idx > -1; --idx) {
					PdfTableCell xcell = wordCells.get(idx);
					
					if (xcell.colIndex > maxColIndex || xcell.colIndex < minColIndex) {
						continue;
					}
					
					if (colIndexSet.contains(Integer.valueOf(xcell.colIndex))) {

						for (int start = xcell.colIndex, end = xcell.colIndex + xcell.getColspan(); start < end; ++start) {
							colIndexSet.remove(Integer.valueOf(start));
						}
						
						// 处理底边
						if (xcell.bottomBorderWidth > 0) {
							BorderRoundRectangle borderRoundRectangle = (BorderRoundRectangle) xcell.getCellEvent();
							borderRoundRectangle.bottomBorder.isDisabled = false;
						}
						
						if (colIndexSet.size() == 0) {
							break;
						}
						
						minColIndex = colIndexSet.first().intValue();
						maxColIndex = colIndexSet.last().intValue();
					}
				}
			}
		}
		
		/**
		 * 处理title,header,detail,footer相连处的边框
		 */
		protected void processCellsBorderForTHDF() {
			int titleFirstRowIndex = (info.title != null && info.title.size() > 0 ? info.title.get(0).rowIndex : -1);
			int headerFirstRowIndex = (info.header != null && info.header.size() > 0 ? info.header.get(0).rowIndex : -1);
			int detailFirstRowIndex = (info.detail != null && info.detail.size() > 0 ? info.detail.get(0).rowIndex : -1);
			int footerFirstRowIndex = (info.footer != null && info.footer.size() > 0 ? info.footer.get(0).rowIndex : -1);
			
			List<List<PdfTableCell>> allRows = new ArrayList<List<PdfTableCell>>(2);
			List<PdfTableCell> dFirstRow = null;
			List<PdfTableCell> fFirstRow = null;
			
			// title
			if (titleFirstRowIndex > -1) {
    			List<PdfTableCell> hdfFirstRow = null;
    			if (headerFirstRowIndex > -1) {
    				hdfFirstRow = getRow(info.header, headerFirstRowIndex);
    			} else if (detailFirstRowIndex > -1) {
    				hdfFirstRow = getRow(info.detail, detailFirstRowIndex);
    				dFirstRow = hdfFirstRow;
    			} else if (footerFirstRowIndex > -1) {
    				hdfFirstRow = getRow(info.footer, footerFirstRowIndex);
    				fFirstRow = hdfFirstRow;
    			}
    			if (hdfFirstRow != null) {
    				allRows.add(info.title);
    				allRows.add(hdfFirstRow);
    				processCellsBorder(hdfFirstRow, allRows);
    			}
			}

			// header
			if (headerFirstRowIndex > -1) {
    			List<PdfTableCell> dfFirstRow = null;
    			if (detailFirstRowIndex > -1) {
    				dfFirstRow = (dFirstRow != null ? dFirstRow : getRow(info.detail, detailFirstRowIndex));
    				dFirstRow = dfFirstRow;
    			} else if (footerFirstRowIndex > -1) {
    				dfFirstRow = (fFirstRow != null ? fFirstRow : getRow(info.footer, footerFirstRowIndex));
    				fFirstRow = dfFirstRow;
    			}
    			if (dfFirstRow != null) {
    				allRows.clear();
    				allRows.add(info.header);
    				allRows.add(dfFirstRow);
    				processCellsBorder(dfFirstRow, allRows);
    			}
			}
			
			// detail 只需要处理分页时上一页底边边框即可@see #processCellsBorderForPaging
			
			// footer 可忽略
			if (footerFirstRowIndex > -1) {
    			List<PdfTableCell> dhtRow = null;
    			if (detailFirstRowIndex > -1) {
    				dhtRow = info.detail;
    			} else if (headerFirstRowIndex > -1) {
    				dhtRow = info.header;
    			} else if (titleFirstRowIndex > -1) {
    				dhtRow = info.title;
    			}
    			if (dhtRow != null) {
    				allRows.clear();
    				allRows.add(dhtRow);
    				
    				fFirstRow = (fFirstRow != null ? fFirstRow : getRow(info.footer, footerFirstRowIndex));
    				
    				allRows.add(fFirstRow);
    				processCellsBorder(fFirstRow, allRows);
    			}
			}
		}
		
		protected List<PdfTableCell> getRow(List<PdfTableCell> rows, int rowIndex) {
			List<PdfTableCell> list = new ArrayList<PdfTableCell>();
			for (int i = 0, j = rows.size(); i < j; ++i) {
				PdfTableCell xcell = rows.get(i);
				if (xcell.rowIndex == rowIndex) {
					list.add(xcell);
				} else if (xcell.rowIndex > rowIndex) {
					break;
				}
			}
			return list;
		}
		
		// 特别处理双线边框
		private void processCellsBorder(List<PdfTableCell> currentRow, List<List<PdfTableCell>> rows) {

			for (int i = 0, j = currentRow.size(), k = rows.size(); i < j; ++i) {
				PdfTableCell xcell = currentRow.get(i);
				
				if (xcell.colIndex > 0 && xcell.leftBorderWidth > 0) {
					int startIndex = i - 1;
					boolean flag = startIndex > -1 ? updateLeftCellBorder(xcell, startIndex, -1, currentRow) : false;
					
					if (!flag || xcell.getRowspan() > 1) {
						startIndex = j - 1;
						flag = startIndex > 1 ? updateLeftCellBorder(xcell, startIndex, i, currentRow) : false;
					}
				}
				
				if (xcell.rowIndex > 0 && xcell.topBorderWidth > 0) {
					
					int startIndex = i - 1;
					boolean flag = startIndex > -1 ? updateTopCellBorder(xcell, startIndex, currentRow) : false;
					
					if (!flag && k > 1) {
						List<PdfTableCell> preRow = rows.get(rows.size() - 2);
						
						updateTopCellBorder(xcell, preRow.size() - 1, preRow);
					}
					
				} // end if top
			}
				
		}
		
		private boolean updateLeftCellBorder(PdfTableCell xcell, int startIndex, int endIndex, List<PdfTableCell> currentRow) {
			
			boolean flag = false;
			
			// 处理合并单元格
			int rowspan = xcell.getRowspan();
			int leftXPos = (int) (xcell.originalXPos + 0.1f);
			for (int preIndex = startIndex, rowIndex = xcell.rowIndex, rowspanIndex = xcell.rowIndex + rowspan - 1; preIndex > endIndex; --preIndex) {
				PdfTableCell leftCell = currentRow.get(preIndex);
				
				if (leftCell.colIndex >= xcell.colIndex) {
					continue;
				}
				
				int rightXPos = (int) (leftCell.originalXPos + leftCell.originalWidth + 0.1f);
				if (rightXPos == leftXPos) {
					if ( (rowIndex >= leftCell.rowIndex && rowIndex <= leftCell.rowIndex + leftCell.getRowspan() - 1)
							|| (rowIndex <= leftCell.rowIndex && leftCell.rowIndex <= rowspanIndex) ) {
						
						flag = true; // found
						
						BorderRoundRectangle leftCellBorder = (BorderRoundRectangle) leftCell.getCellEvent();

						if (leftCellBorder != null) {

							BorderRoundRectangle currentCellBorder = (BorderRoundRectangle) xcell.getCellEvent();
        					if (currentCellBorder.getLeftBorder() instanceof DoubleBorderStyle) {
        						
        						DoubleBorderStyle currentCellDoubleBorder = (DoubleBorderStyle) currentCellBorder.getLeftBorder();
        						if (leftCellBorder.getTopBorder() instanceof DoubleBorderStyle) {
        							if (rowIndex == leftCell.rowIndex) {
        								currentCellDoubleBorder.drawOutTopOrLeft = false;
        							}
        							
        							if (leftCellBorder.getRightBorder() instanceof DoubleBorderStyle) {
            							DoubleBorderStyle leftCellDoubleBorder = (DoubleBorderStyle) leftCellBorder.getTopBorder();
            							leftCellDoubleBorder.drawOutBottomOrRight = true;
            							leftCellDoubleBorder.drawInnerBottomOrRight = true;
        							}
        						}
        						
        						if (leftCellBorder.getBottomBorder() instanceof DoubleBorderStyle) {
        							if (rowspanIndex == leftCell.rowIndex + leftCell.getRowspan() - 1) {
        								PdfBorderStyle bottomBorder = currentCellBorder.getBottomBorder();
        								if (bottomBorder instanceof DoubleBorderStyle && !bottomBorder.isDisabled) {
        									currentCellDoubleBorder.drawOutBottomOrRight = false;
        								}
        							}
        							
        							if (leftCellBorder.getRightBorder() instanceof DoubleBorderStyle) {
        								DoubleBorderStyle leftCellDoubleBorder = (DoubleBorderStyle) leftCellBorder.getBottomBorder();
        								leftCellDoubleBorder.drawOutBottomOrRight = true;
        								leftCellDoubleBorder.drawInnerBottomOrRight = true;
        							}
        						}
        					}
        					
        					// 只处理双线
        					if (leftCellBorder.getRightBorder() instanceof DoubleBorderStyle/*leftCell.rightBorderWidth > 0*/) {
        						leftCellBorder.getRightBorder().isDisabled = true;
        					}
        					
        					// 如果左右两个格子拼接处是同种类型的边框, 去掉一边边框, 从而使得边框叠加绘制效果更好. Excel多行单元格拼接比其行数少的单元格时按少行的边框为准
        					if (leftCellBorder.rightBorder != null) {
            					if (leftCell.rowIndex == xcell.rowIndex 
            							&& leftCell.getRowspan() >= xcell.getRowspan() 
            							&& leftCellBorder.rightBorder.getClass() == currentCellBorder.leftBorder.getClass()) {
            						leftCellBorder.rightBorder.isDisabled = true;
            					}
        					}
						}
    					
    					rowspan -= leftCell.getRowspan();
    					if (rowspan < 1) {
    						break;
    					}
					}
				}
			}
			
			return flag;
		}
		
		private boolean updateTopCellBorder(PdfTableCell xcell, int startIndex, List<PdfTableCell> currentRow) {
			
			boolean flag = false;
			
			// 处理合并单元格
			int colspan = xcell.getColspan();
			
			int topYPos = (int) (xcell.originalYPos + 0.1f);
			for (int preIndex = startIndex, colIndex = xcell.colIndex, colspanIndex = xcell.colIndex + colspan - 1; preIndex > -1; --preIndex) {
				PdfTableCell topCell = currentRow.get(preIndex);
				
				if (topCell.rowIndex >= xcell.rowIndex || topCell.colIndex > colspanIndex) {
					continue;
				}
				
				int bottomYPos = (int) (topCell.originalYPos + topCell.originalHeight + 0.1f);
				if (bottomYPos == topYPos) {
				
					if ( (colIndex >= topCell.colIndex && colIndex <= topCell.colIndex + topCell.getColspan() - 1)
						|| (colIndex <= topCell.colIndex && topCell.colIndex <= colspanIndex) ) {

						flag = true; // found
						
						// 双线边框特殊处理
						BorderRoundRectangle topCellBorder = (BorderRoundRectangle) topCell.getCellEvent();
						if (topCellBorder != null) {
							
    						BorderRoundRectangle currentCellBorder = (BorderRoundRectangle) xcell.getCellEvent();
    						if (currentCellBorder.getTopBorder() instanceof DoubleBorderStyle) {
    							DoubleBorderStyle currentCellDoubleBorder = (DoubleBorderStyle) currentCellBorder.getTopBorder();
    							
    							if (topCellBorder.getLeftBorder() instanceof DoubleBorderStyle) {
    								if (colIndex == topCell.colIndex) {
    									currentCellDoubleBorder.drawOutTopOrLeft = false; // 如果双线边上下对齐, 则去掉下边双线上外线的缝合部分
    								}
    								
    								if (topCellBorder.getBottomBorder() instanceof DoubleBorderStyle) {
        								DoubleBorderStyle topCellDoubleBorder = (DoubleBorderStyle) topCellBorder.getLeftBorder();
        								topCellDoubleBorder.drawInnerBottomOrRight = true;
        								topCellDoubleBorder.drawOutBottomOrRight = true;
    								}
    							}
    							
    							if (topCellBorder.getRightBorder() instanceof DoubleBorderStyle) {
    								if (colspanIndex == topCell.colIndex + topCell.getColspan() - 1) {
    									PdfBorderStyle rightBorder = currentCellBorder.getRightBorder();
    									if (rightBorder instanceof DoubleBorderStyle && !rightBorder.isDisabled) {
    										currentCellDoubleBorder.drawOutBottomOrRight = false;
    									}
    								}
    								
    								if (topCellBorder.getBottomBorder() instanceof DoubleBorderStyle) {
        								DoubleBorderStyle topCellDoubleBorder = (DoubleBorderStyle) topCellBorder.getRightBorder();
        								topCellDoubleBorder.drawInnerBottomOrRight = true;
        								topCellDoubleBorder.drawOutBottomOrRight = true;
    								}
    							}
    						}
    						
    						// 只处理双线
    						if (topCellBorder.getBottomBorder() instanceof DoubleBorderStyle/*topCell.bottomBorderWidth > 0*/) {
    							topCellBorder.getBottomBorder().isDisabled = true;
    						}
    						
        					// 如果上下两个格子拼接处是同种类型的边框, 去掉一边边框, 从而使得边框叠加绘制效果更好
    						if (topCellBorder.bottomBorder != null) {
            					if (topCell.colIndex >= xcell.colIndex 
            							&& topCellBorder.bottomBorder.getClass() == currentCellBorder.topBorder.getClass()) {
            						topCellBorder.bottomBorder.isDisabled = true;
            					}
    						}
						}
						
						colspan -= topCell.getColspan();
						if (colspan < 1) {
							break;
						}
					}
				}
			}
			
			return flag;
		}

		private void recalculateCellsForTextControl(List<PdfTableCell> row_, PdfTableCellPos currentPos, Flag flag) {
			ReportExcelPdfTable rept = this.getReportExcelPdfTable();
			
			Map<Integer, PdfTableCellPos> rowIndexPosMapping = info.rowIndexPosMapping;
			
			Map<Integer, List<PdfTableCell>> mergeCellsMap = new LinkedHashMap<Integer, List<PdfTableCell>>();
			List<PdfTableCell> rowIndexCells = new ArrayList<PdfTableCell>();
			float maxChangeHeight = 0;
			int preRowIndex = row_.get(0).rowIndex;
			boolean hasChange = false;
			
			for (int i = 0, j = row_.size(); i < j; ++i) {
				PdfTableCell xcell = row_.get(i);
				if (xcell.rowIndex == preRowIndex) {
					rowIndexCells.add(xcell);
					
					float cellOriginalHeight = xcell.getMaxHeight();
					
					rept.setTextDrawControl(xcell);
					
					float cellNewHeight = xcell.getMaxHeight();
					if (cellOriginalHeight < cellNewHeight) {
						xcell.setFixedHeight(cellOriginalHeight);
						float changeHeight = cellNewHeight - cellOriginalHeight;
						if (changeHeight > maxChangeHeight) {
							maxChangeHeight = changeHeight;
						}
					}
					
					if (xcell.getRowspan() > 1) {
						Integer key = Integer.valueOf(xcell.rowIndex);
						List<PdfTableCell> mergeCells = mergeCellsMap.get(key);
						if (mergeCells == null) {
							mergeCells = new ArrayList<PdfTableCell>();
							mergeCellsMap.put(key, mergeCells);
						}
						PdfTableCell tempCell = new PdfTableCell();
						tempCell.setFixedHeight(cellOriginalHeight);
						mergeCells.add(tempCell);
						mergeCells.add(xcell);
					}
				} else {
					if (maxChangeHeight > 0) {
						hasChange = true;
						changeCellHeight(rowIndexCells, mergeCellsMap, rowIndexPosMapping, maxChangeHeight);
					}
					
					--i;
					rowIndexCells = new ArrayList<PdfTableCell>();
					maxChangeHeight = 0;
					preRowIndex = xcell.rowIndex;
				}
			}
			
			// 处理最后的格子
			if (rowIndexCells.size() > 0) {
				if (maxChangeHeight > 0) {
    				hasChange = true;
    				changeCellHeight(rowIndexCells, mergeCellsMap, rowIndexPosMapping, maxChangeHeight);
				}
			}
			
			if (hasChange) {
				flag.attr.put(TEXT_CONTROL, Boolean.TRUE);
			}
		}
		
		private void changeCellHeight(List<PdfTableCell> rowIndexCells, 
		                              Map<Integer, List<PdfTableCell>> mergeCellsMap, 
		                              Map<Integer, PdfTableCellPos> rowIndexPosMapping, 
		                              float maxChangeHeight) {
			for (PdfTableCell cell : rowIndexCells) {
				cell.setFixedHeight(cell.getMaxHeight() + maxChangeHeight);
			}
			
			// 前面合并单元格如果有包含此rowIndex的所有格子的大小也需要增加
			if (mergeCellsMap.size() > 0) {
				int currentRowIndex = rowIndexCells.get(0).rowIndex;
				PdfTableCellPos currentRowIndexPos = rowIndexPosMapping.get(Integer.valueOf(currentRowIndex));
				float lastPos = currentRowIndexPos.yPos + currentRowIndexPos.height;
				
				for (Iterator<Integer> iter = mergeCellsMap.keySet().iterator(); iter.hasNext(); ) {
					Integer key = iter.next();
					if (key.intValue() < currentRowIndex) {
						// 原始Y坐标和高度比较看是否包含
						PdfTableCellPos preRowIndexPos = rowIndexPosMapping.get(key);
						float prePos = preRowIndexPos.yPos;

						List<PdfTableCell> mergeCells = mergeCellsMap.get(key);
						for (int cellIndex = 0, mergeCellsSize = mergeCells.size(); cellIndex < mergeCellsSize; cellIndex += 2) {
							PdfTableCell tempCell = mergeCells.get(cellIndex);
							float h = tempCell.getMaxHeight();
							if (lastPos < prePos + h + 1) { // 可以增加判断colIndex在左边的条件
								PdfTableCell mergeCell = mergeCells.get(cellIndex + 1);
								mergeCell.setFixedHeight(h + maxChangeHeight);
							}
						}
					}
				}
			}
		}

		protected void calculateCellsPos(float splitHeight) {
			if (splitHeight < 1) {
				return;
			}
			
			// 处理title最下面的边框和header最下面的边框
			processCellsBorderForTHDF();
	
			splitHeight = toRoundHalfUpFloat(splitHeight, 2);

			Flag flag = new Flag();
			float contentSplitHeight = drawBodyHeight; //(drawBodySplitHeight > splitHeight || drawBodySplitHeight < 1 ? splitHeight : drawBodySplitHeight);
			flag.contentSplitHeight = contentSplitHeight;
			flag.contentSplitOffset = splitHeight - contentSplitHeight;
			flag.currentMaxHeight = splitHeight;
			flag.currentHeight = 0;
			flag.offsetHeight = 0f;
			flag.offsetRowIndex = 0;
			flag.splitHeight = splitHeight;
			
			List<PdfTableCell> pdfCells = info.cells;
			
			if (info.title != null && info.title.size() > 0) {
				calculateRow(info.title, flag);
				pdfCells.addAll(info.title);
				flag.currentHeight += calculateHeight(info.title, true);

				flag.lastCell = info.title.get(info.title.size() - 1);
			}

			if (info.header != null && info.header.size() > 0) {
				calculateRow(info.header, flag);
				pdfCells.addAll(info.header);
				flag.hasHeader = true;
				flag.headerHeight = calculateHeight(info.header, true);
				flag.headerRowspan = calculateRowspan(info.header) - calculateRowspan(info.title);
				
				flag.currentHeight += flag.headerHeight;
				
				flag.lastCell = info.header.get(info.header.size() - 1);
			}
			
			flag.currentHeight = toRoundHalfUpFloat(flag.currentHeight, 2);
			if (flag.currentMaxHeight <= flag.currentHeight) {
				int n = (int) (flag.currentHeight / flag.splitHeight) + 1;
				flag.currentMaxHeight = n * splitHeight;
			}
			
			List<PdfTableCell> detail = info.detail;
			if (detail != null && detail.size() > 0) {
				addRow(pdfCells, detail, flag);
			}
			
			if (info.footer != null && info.footer.size() > 0) {
				recalculateFooterYPos(pdfCells, flag);
			}
			
			// 更新info
			info.rowIndexPosMapping = flag.rowIndexPosMapping;
			if (flag.pageNumHeaderMapping != null && flag.pageNumHeaderMapping.size() > 0) {
				HeaderInfo headerInfo = new HeaderInfo();
				headerInfo.pageNumHeaderMapping = flag.pageNumHeaderMapping;
				headerInfo.headerRowspan = flag.headerRowspan;
				headerInfo.headerHeight = flag.headerHeight;
				info.headerInfo = headerInfo;
			}
		}
		
		private void recalculateFooterYPos(List<PdfTableCell> pdfCells, Flag flag) {
			float offsetHeight = flag.offsetHeight;
			
			flag.offsetHeight = 0;
			float newStartYPos = flag.currentHeight;
			int newRowIndex = flag.lastCell.rowIndex + flag.lastCell.getRowspan();
			
			PdfTableCell firstCell = info.footer.get(0);
		    int rowIndexOffset = firstCell.rowIndex;
		    float yPosOffset = firstCell.yPos;
			
			for (PdfTableCell cell : info.footer) {
				Integer colkey = Integer.valueOf(cell.colIndex);
				PdfTableCellPos colValue = info.colIndexPosMapping.get(colkey);
				if (colValue != null) {
					cell.xPos = colValue.xPos;
				} else {
					float xPos = info.calculateXPos(cell);
					colValue = new PdfTableCellPos();
					colValue.xPos = xPos;
					info.colIndexPosMapping.put(colkey, colValue);
					cell.xPos = xPos;
				}
				
				cell.yPos = cell.yPos - yPosOffset + newStartYPos;
				
				float width = info.getCellWidth(cell.colIndex, cell.getColspan());
				cell.maxWidth = width;
				
				cell.drawWidth = cell.maxWidth;
				cell.drawHeight = cell.getMaxHeight();
				
				Integer rowkey = Integer.valueOf(cell.rowIndex - rowIndexOffset + newRowIndex);
				PdfTableCellPos rowValue = info.rowIndexPosMapping.get(rowkey);
				if (rowValue == null) {
					rowValue = new PdfTableCellPos();
					rowValue.cell = cell;
					rowValue.height = cell.getMaxHeight();
					rowValue.yPos = cell.yPos;
					info.rowIndexPosMapping.put(rowkey, rowValue);
				} else if (rowValue.height > cell.getMaxHeight()) {
					rowValue.height = cell.getMaxHeight(); // 最小高度
				}
			}
			
			addRow(pdfCells, info.footer, flag);
			
			flag.offsetHeight = flag.offsetHeight + offsetHeight;
		}

		private void addRow(List<PdfTableCell> pdfCells, List<PdfTableCell> cells, Flag flag) {
		    
			PdfTableCellPos currentPos = new PdfTableCellPos();
			PdfTableCell firstCell = cells.get(0);
			currentPos.cell = firstCell;
			currentPos.height = firstCell.getMaxHeight();
			currentPos.yPos = firstCell.yPos;

			List<PdfTableCell> row_ = new ArrayList<PdfTableCell>();

			for (int cellIndex = 0, cellSize = cells.size(), currentRowIndex = firstCell.rowIndex; cellIndex < cellSize; cellIndex++) {
				PdfTableCell xcell = cells.get(cellIndex);
				if (currentRowIndex == xcell.rowIndex) {
					row_.add(xcell); // 找出按一行高度最大的格子为行高的一行全部格子, 并获得最大高度信息
					if (xcell.getMaxHeight() > currentPos.height) {
						currentPos.cell = xcell;
						currentPos.height = xcell.getMaxHeight();
					}
				} else if (xcell.yPos + xcell.getMaxHeight() < currentPos.yPos + currentPos.height + 1) { // +1避免浮点误差
					row_.add(xcell);
				} else if (xcell.yPos < currentPos.yPos + currentPos.height - 1f) {
					row_.add(xcell);
					currentPos.height += (xcell.yPos + xcell.getMaxHeight() - currentPos.yPos - currentPos.height);
				} else {

					addRow(pdfCells, row_, currentPos, flag);
					flag.lastCell = row_.get(row_.size() - 1);
					
					currentRowIndex = xcell.rowIndex;
					--cellIndex;
					row_ = new ArrayList<PdfTableCell>();
					
					currentPos = new PdfTableCellPos();
					currentPos.cell = xcell;
					currentPos.height = xcell.getMaxHeight();
					currentPos.yPos = xcell.yPos;
				}
			}
			
			// 处理最后的格子
			if (row_.size() > 0) {
				addRow(pdfCells, row_, currentPos, flag);
			}
			
			flag.lastCell = cells.get(cells.size() - 1);
		}
		
		private void addRow(List<PdfTableCell> pdfCells, 
		                    List<PdfTableCell> row_, 
		                    PdfTableCellPos currentPos, 
		                    Flag flag) {
			
			// 按行为单位绘制
			float h = currentPos.yPos + currentPos.height + flag.offsetHeight;
			flag.directSplitStartHeight = 0;
			if (h <= flag.currentMaxHeight) {
				// 能容纳下内容的高度
				flag.currentHeight += currentPos.height;
				
				calculateRow(row_, flag);
				
				pdfCells.addAll(row_);
			} else if (h > flag.currentMaxHeight && currentPos.height <= flag.splitHeight - flag.headerHeight) {
				// 下一页能容纳下内容的高度(header部分也能容下)
				
				// 处理分页处上一页最后一行表格的底部边框
				processCellsBorderForPaging(pdfCells);
				
				float newOffset = toRoundHalfUpFloat(flag.currentMaxHeight - flag.currentHeight, 2);
				float newStartYPos = flag.currentMaxHeight;
				flag.offsetHeight += (newOffset + flag.headerHeight);
				flag.currentHeight = toRoundHalfUpFloat(flag.currentMaxHeight + currentPos.height + flag.headerHeight, 2);
				flag.currentMaxHeight += flag.splitHeight;
				
				if (flag.hasHeader) {
					int newRowIndex = flag.lastCell.rowIndex + flag.lastCell.getRowspan(); // header之前最后一行的rowIndex
					List<PdfTableCell> newHeaderRow = createHeaderAndReCalculateYPos(flag, newStartYPos, newRowIndex);
					pdfCells.addAll(newHeaderRow);
					flag.offsetRowIndex += flag.headerRowspan;
				}
				
				calculateRow(row_, flag);
				
				pdfCells.addAll(row_);
			} else {
				// 直接行切
				// @see #calculateRow 需要判断flag.hasHeader
				float diffHeight = currentPos.height - (flag.currentMaxHeight - flag.currentHeight);
				float contentSplitHeight = flag.hasHeader ? flag.contentSplitHeight - flag.headerHeight : flag.contentSplitHeight;
				int n = (int) (diffHeight / contentSplitHeight);
				float m = remainderToFloat(diffHeight, contentSplitHeight); //diffHeight % contentSplitHeight;
				flag.currentHeight = (flag.currentMaxHeight + flag.contentSplitOffset) + (n * flag.splitHeight) + (flag.hasHeader ? flag.headerHeight + m : m);
				flag.directSplitStartHeight = flag.currentMaxHeight;
				
				if (flag.hasHeader) {
					// 动态构造表头(外层格子的newRowIndex相当于没有变化)
					Flag tmpFlag = new Flag();
					
					int pageNum = (int)(flag.currentHeight / flag.splitHeight) + 1 - n;
					for (int i = 0, j = n + 1; i < j; ++i, ++pageNum) {
						float newStartYPos = (pageNum - 1) * flag.splitHeight;
						int newRowIndex = i * flag.headerRowspan;
						List<PdfTableCell> newHeaderRow = createHeaderAndReCalculateYPos(tmpFlag, newStartYPos, newRowIndex);
						
						// 处理分页处上一页最后一行表格的底部边框
						processCellsBorderForPaging(newHeaderRow);
						
						flag.pageNumHeaderMapping.put(Integer.valueOf(pageNum), newHeaderRow);
					}
				}
				
				n = (int) (flag.currentHeight / flag.splitHeight) + 1;
				flag.currentMaxHeight = n * flag.splitHeight - flag.contentSplitOffset;
				
				// 在计算前先计算偏移
				float offset = calculateRow(row_, flag);
				
				flag.offsetHeight += offset;
				
				pdfCells.addAll(row_);
			}
		}
		
		private List<PdfTableCell> createHeaderAndReCalculateYPos(Flag flag, float newStartYPos, int newRowIndex) {
			PdfTableCell firstCell = info.header.get(0);
			float yPosOffset = firstCell.yPos;
			int rowIndexOffset = firstCell.rowIndex;
			
			Map<Integer, PdfTableCellPos> flagMap = flag.rowIndexPosMapping;
			
			List<PdfTableCell> result = new ArrayList<PdfTableCell>();
			for (PdfTableCell cell : info.header) {
				PdfTableCell newCell = PdfTableCell.copyCell(cell);
				newCell.rowIndex = cell.rowIndex - rowIndexOffset + newRowIndex;
				newCell.colIndex = cell.colIndex;
				newCell.xPos = cell.xPos;
				newCell.yPos = cell.yPos - yPosOffset + newStartYPos;

				result.add(newCell);
				
				Integer rowKey = Integer.valueOf(newCell.rowIndex);
				PdfTableCellPos rowValue = flagMap.get(rowKey);
				if (rowValue == null) {
					rowValue = new PdfTableCellPos();
					rowValue.cell = newCell;
					rowValue.height = newCell.getMaxHeight();
					rowValue.yPos = newCell.yPos;
					flagMap.put(rowKey, rowValue);
				} else if (rowValue.height > cell.getMaxHeight()) {
					rowValue.height = cell.getMaxHeight(); // 最小高度
				}
			}
			
			return result;
		}
		
		private float calculateRow(List<PdfTableCell> row, Flag flag) {
			float offsetHeight = flag.offsetHeight;
			int offsetRowIndex = flag.offsetRowIndex;
			Map<Integer, PdfTableCellPos> infoMap = info.rowIndexPosMapping;
			Map<Integer, PdfTableCellPos> flagMap = flag.rowIndexPosMapping;

			float splitHeight = flag.hasHeader ? flag.splitHeight - flag.headerHeight : flag.splitHeight;
			float directSplitStartHeight = flag.directSplitStartHeight;
			float contentSplitOffset = flag.hasHeader ? flag.headerHeight + flag.contentSplitOffset : flag.contentSplitOffset;
			float directSplitHeightOffset = 0;
			float rowTopYPos = 0;
			float rowMaxHeight = 0;
			
			for (int i = 0, j = row.size(), k = -1; i < j; i++) {
				PdfTableCell cell = row.get(i);
				
				if (cell.getMaxHeight() > rowMaxHeight) {
					rowTopYPos = cell.yPos;
					rowMaxHeight = cell.getMaxHeight();
				}
				
				cell.yPos += offsetHeight;
				
				if (cell.rowIndex != k) {
					k = cell.rowIndex;
					PdfTableCellPos cellPos = infoMap.get(Integer.valueOf(cell.rowIndex));
					
					if (directSplitStartHeight > 0 && cell.yPos >= directSplitStartHeight) {
						int n = (int)((cell.yPos - directSplitStartHeight + directSplitHeightOffset) / splitHeight + 1);
						directSplitHeightOffset = n * contentSplitOffset;
						cell.yPos += directSplitHeightOffset;
					}
					cellPos.yPos = cell.yPos;
					
					cell.rowIndex = k + offsetRowIndex;
					flagMap.put(Integer.valueOf(cell.rowIndex), cellPos);
				} else {
					cell.rowIndex = k + offsetRowIndex;
					
					if (directSplitStartHeight > 0 && directSplitHeightOffset > 0) {
						cell.yPos += directSplitHeightOffset;
					}
				}
			}
			
			if (directSplitStartHeight > 0 && directSplitHeightOffset == 0) {
				float lastYPos = rowTopYPos + rowMaxHeight;
				if (lastYPos > directSplitStartHeight) {
					int n = (int)((lastYPos - directSplitStartHeight) / splitHeight + 1);
					directSplitHeightOffset = n * contentSplitOffset;
				}
			}
			
			return directSplitHeightOffset;
		}

		class Flag {
			float currentMaxHeight;
			float currentHeight;
			float offsetHeight;
			int offsetRowIndex;
			float splitHeight;
			float directSplitStartHeight;
			float contentSplitHeight;
			float contentSplitOffset;
			boolean hasHeader;
			int headerRowspan;
			float headerHeight;
			PdfTableCell lastCell;
			Map<Integer, PdfTableCellPos> rowIndexPosMapping = new HashMap<Integer, PdfTableCellPos>();
			Map<Integer, List<PdfTableCell>> pageNumHeaderMapping = new HashMap<Integer, List<PdfTableCell>>();
			Map<String, Object> attr;
		}
		
	}
	
	//==================================================
	// Enum
	//==================================================
	enum TextControlEnum {
		DIRECT, STRETCH, SHRINK, CLIP, AUTOSIZE;
	};
	
	//==================================================
	// Static Field/Method
	//==================================================
	private static final float UNIT_PX_TO_PT_FAC = 0.75f;
	private static final float UNIT_INCH_TO_PT_FAC = 72f;
	private static final String FONT_HELVETICA = "Helvetica";
	private static final String FONT_STSONG = "STSongStd-Light";
	private static final String FONT_MSUNG = "MSung-Light";
	private static final String FONT_SEGOE_UI = "segoe ui symbol";
	private static final String FONT_ENCODING_UNIGB_UCS2_H = "UniGB-UCS2-H";
	private static final String FONT_ENCODING_UNICNS_UCS2_H = "UniCNS-UCS2-H";
	
	//private static final int DEFAULT_PHF_FONT_SIZE = 11;
	
	/**
	 * 取余数. 避免1460.4 % 486.80002 = 486.79996
	 * @param a
	 * @param b
	 * @return
	 */
	private static float remainderToFloat(double a, double b) {
		BigDecimal a0 = new BigDecimal(a);
		a0 = a0.setScale(2, BigDecimal.ROUND_HALF_UP);
		BigDecimal b0 = new BigDecimal(b);
		b0 = b0.setScale(2, BigDecimal.ROUND_HALF_UP);
		BigDecimal c0 = a0.remainder(b0);
		return c0.floatValue();
	}
	
	/**
	 * 处理浮点数. 2.504->2.50, 2.505->2.51
	 * @param a
	 * @param scale
	 * @return
	 */
	private static float toRoundHalfUpFloat(double a, int scale) {
		BigDecimal a0 = new BigDecimal(a);
		a0 = a0.setScale(scale, BigDecimal.ROUND_HALF_UP);
		return a0.floatValue();
	}
	
	/**
	 * 获得默认处理文字的选择器
	 * @param fontSize
	 * @param fontColor 如果为null就是黑色
	 * @return
	 * @throws DocumentException
	 * @throws IOException
	 */
	private static FontSelector getDefaultFontSelector(float fontSize, BaseColor fontColor) {
		FontSelector selector = new FontSelector();

		Font firstFont = FontFactory.getFont(FONT_HELVETICA, fontSize); // 黑体
		Font f2 = FontFactory.getFont(FONT_STSONG, FONT_ENCODING_UNIGB_UCS2_H, BaseFont.NOT_EMBEDDED, fontSize); // 简体宋体
		Font f3 = FontFactory.getFont(FONT_MSUNG, FONT_ENCODING_UNICNS_UCS2_H, BaseFont.NOT_EMBEDDED, fontSize);    // 繁体宋体
		
		selector.addFont(firstFont);
		selector.addFont(f2);
		selector.addFont(f3);
		
		if (fontColor != null) {
			firstFont.setColor(fontColor);
			f2.setColor(fontColor);
			f3.setColor(fontColor);
		}
		
		//f3.setStyle(Font.BOLD);
		
		return selector;
	}

	// private methods
	private static boolean isValidReport(Report report) {
		// report.isEmptyReport() 也要输出页眉页脚的一面空白页
		return !(report == null);
	}

//	private static byte[] copy(byte[] source) {
//
//		byte[] data = new byte[source.length];
//		System.arraycopy(source, 0, data, 0, source.length);
//		return data;
//	}
//
//	private static XSSFWorkbook toExcel(byte[] middle) throws Exception {
//
//		ByteArrayInputStream in = new ByteArrayInputStream(middle);
//		try {
//			XSSFWorkbook excel = new XSSFWorkbook(in);
//			return excel;
//		} finally {
//			in.close();
//		}
//	}
	
}
